10 Replies Latest reply on May 7, 2007 5:28 AM by peterent

    dataProvider for each DataGridColumn

    cialeen
      Hi, all

      How can I give a dataProvider for each DataGridColumn? Because I have an arrayCollection with arrays and each array will be assigned to one dataGridColumn. But the problem is the value of dataField are the same for all columns, so at the end I only got the values from the last array for the whole dataGrid. By the way the number of columns depends on the length of the arrayCollection and it is dynamic.

      How can I write a custom DataGridColumn for it?

      Anybody got any ideas?

      Thank you very much.
        • 1. Re: dataProvider for each DataGridColumn
          peterent Level 2
          You can use an itemRenderer for each column and it could pick out the information that it needs.
          • 2. dataProvider for each DataGridColumn
            cialeen Level 1
            Peter, thank you for your reply. But I tried using itemrenderer, it doesn't work. I gave dataProvider as a property to each column, so I hope the data will read from each different dataProvider. However, it overwrites the previous as well.

            My code is as follows: (all data point in the dataProvider has date and value properies)

            DataGridItemRenderer:
            public class CustomDataGridItemRenderer extends DataGridItemRenderer
            {
            public var dataProvider:Array = new Array;
            public function CustomDataGridItemRenderer ()
            {
            super();
            }

            override public function validateProperties():void{

            super.validateProperties();

            var array:Array= new Array;
            for each (var ob:ObjectProxy in dataProvider)
            {
            array.push(ob.date);
            }

            var index:int = array.indexOf(this.data.date, 0);
            this.text = dataProvider[index].value;
            }

            }

            DataGrid:

            for ( var index:int = 0 ; index < trendData.length; index)
            {
            var _dataColumn:DataGridColumn = new DataGridColumn();
            _dataColumn.dataField = "value";
            _dataColumn.setStyle("color", legendItem.LegendItemColor);

            var classFactory:ClassFactory = new ClassFactory(CustomDataGridItemRenderer);
            classFactory.properties = {dataProvider:trendData[index]}
            _dataColumn.itemRenderer = classFactory;

            dataGrid.columns.push (_dataColumn);
            }

            At the end, I still got same data for all value column. please, please tell me what I did wrong here.

            Thank you in advance.
            • 3. Re: dataProvider for each DataGridColumn
              peterent Level 2
              Let's say you have data like this:

              [ [1,2,3,4], [10,20,30,40], [100,200,300,400] ] which is an Array of Arrays. For your original question this would be a DataGrid with 3 columns and 4 rows. You would set up your DataGrid like this:

              <mx:DataGrid id="grid" dataProvider="{arrayOfArrays}" ... >

              Each DataGridColumn simply gets the same itemRenderer:

              dgColumn.itemRenderer = new ClassFactory( ArrayItemRenderer );

              The trick is the ArrayItemRenderer. For that you can extend the Text or Label control:

              public class ArrayItemRenderer extends Label {

              override public function updateDisplayList( unscaledWidth, unscaledHeight ) : void {
              super.updateDisplayList( unscaledWidth, unscaledHeight );
              if( data ) {
              var dp:Array = (listData.owner as DataGrid).dataProvider as Array;
              var colIndex:int = listData.columnIndex;
              var rowIndex:int = listData.rowIndex;
              var colArray:Array = dp[colIndex] as Array;
              text = String(colArray[rowIndex-1]);
              } else {
              text = "";
              }
              } // end of updateDisplayList
              } // end of class

              Take a look at these statements from updateDisplayList:
              var dp:Array = (listData.owner as DataGrid).dataProvider as Array;
              var colIndex:int = listData.columnIndex;
              var rowIndex:int = listData.rowIndex;
              var colArray:Array = dp[colIndex] as Array;
              text = String(colArray[rowIndex-1]);

              colIndex and rowIndex come from listData which is provided to the itemRenderer by the DataGrid. Let's say colIndex is 1 and rowIndex = 2. This means you should see 30 in the cell. Each Array in the dataProvider is supposed to correspond to an entire column, right? So the columnIndex tells you which Array to use. The rowIndex then tells you which item from the column to display.

              That should get you want you want.
              • 4. Re: dataProvider for each DataGridColumn
                cialeen Level 1
                Thank you again, Peter.

                However, I tried the way you said but it doesn't work. If I got an array of arrays and assign it to the dataGrid, the rowIndex will depend on the length of outter array rather than the length of each inner array. Take the example you gave, [ [1,2,3,4], [10,20,30,40], [100,200,300,400] ] , the rowIndex is 3 not 4. So I still can not get the result from that.

                If I still extend the itemRenderer from DataGridItemRenderer, and I can assign the dataProvider to each column. But the column becomes unsortable.

                itemRenderer:
                public class AqDataGridItemRenderer extends DataGridItemRenderer
                {
                public var dataProvider:Array = new Array;

                public function AqDataGridItemRenderer()
                {
                super();
                }

                override public function validateProperties():void{
                super.validateProperties();
                this.text = super.data.amout;
                }

                override public function set data(value:Object):void
                { super.data = dataProvider[this.listData.rowIndex];
                }
                }

                DataGrid: ( Each array in the array has the same properties: date and amount. Besides, the values of date column are the same, so I only display one date column)
                private function create(trendData:Array, legendItemArray:Array):void
                {
                var dataColumns:ArrayCollection = new ArrayCollection();
                var index:int = 0;

                dataGrid.dataProvider = trendData[0];

                var dateColumn:DataGridColumn = new DataGridColumn();
                dateColumn.dataField = "date";
                dataColumns.addItemAt(dateColumn, 0);

                try
                {
                for each ( var legendItem:CustomLegendItem in legendItemArray)
                {
                var _dataColumn:DataGridColumn = new DataGridColumn();

                _dataColumn.dataField = "amount";

                var classFactory:ClassFactory = new ClassFactory(AqDataGridItemRenderer);
                classFactory.properties = {dataProvider:trendData[index++]}
                _dataColumn.itemRenderer = classFactory;

                dataColumns.addItem(_dataColumn);
                }

                dataGrid.columns = dataColumns.source;

                }
                catch(e:Error)
                {}
                }

                When I tried to sort the "amount" columns, the sort order is totally depending on the date column rather than the "amount" itself.
                I don't know why. Could you give me some more hints, please?

                Thank you very much.

                Regards
                • 5. Re: dataProvider for each DataGridColumn
                  peterent Level 2
                  I'm pasting the code I used. That's the best I can do for you.

                  First is the main application:
                  ----------------------------------------
                  <?xml version="1.0" encoding="utf-8"?>
                  <mx:Application xmlns:mx=" http://www.adobe.com/2006/mxml" layout="absolute">

                  <mx:Script>
                  <![CDATA[
                  import mx.collections.ArrayCollection;

                  private function mouseIn( event:MouseEvent ) : void
                  {
                  trace("In: target="+event.target+"; current="+event.currentTarget+"; related="+event.relatedObject);
                  if( event.relatedObject == box.parent ) {
                  b1.visible = true;
                  b2.visible = true;
                  }
                  }

                  private function mouseOut( event:MouseEvent ) : void
                  {
                  trace("out: target="+event.target+"; current="+event.currentTarget+"; related="+event.relatedObject);
                  if( event.relatedObject == box.parent ) {
                  b1.visible = false;
                  b2.visible = false;
                  }
                  }

                  [Bindable] private var dp:ArrayCollection = new ArrayCollection(
                  [ [1,2,3,4], [10,20,30,40], [100,200,300,400] ]);
                  ]]>
                  </mx:Script>
                  <mx:VBox id="box" x="43" y="45" width="300" height="185" horizontalAlign="center"
                  mouseOver="mouseIn(event)"
                  mouseOut="mouseOut(event)"
                  backgroundColor="#804040" verticalAlign="middle">
                  <mx:Button width="127" height="97" />
                  <mx:Button label="Button 1" id="b1" visible="false"/>
                  <mx:Button label="Button 2" id="b2" visible="false"/>
                  </mx:VBox>

                  <mx:DateChooser
                  selectableRange="{{rangeStart : new Date(2000,0,1),rangeEnd : new Date()}}"
                  id="dateChoose" showToday="false"/>

                  <mx:DataGrid x="163" y="292" height="179" width="326" dataProvider="{dp}">
                  <mx:columns>
                  <mx:DataGridColumn headerText="Column 1" dataField="col1" itemRenderer="components.forums.ArrayItemRenderer"/>
                  <mx:DataGridColumn headerText="Column 2" dataField="col2" itemRenderer="components.forums.ArrayItemRenderer"/>
                  <mx:DataGridColumn headerText="Column 3" dataField="col3" itemRenderer="components.forums.ArrayItemRenderer"/>
                  </mx:columns>
                  </mx:DataGrid>

                  </mx:Application>
                  -----------------------------


                  This is the item renderer code:
                  ----------------------------
                  package components.forums
                  {
                  import mx.controls.Text;
                  import mx.collections.ArrayCollection;
                  import mx.controls.DataGrid;

                  public class ArrayItemRenderer extends Text
                  {
                  public function ArrayItemRenderer()
                  {
                  super();
                  }

                  override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
                  {
                  super.updateDisplayList(unscaledWidth, unscaledHeight);
                  if( data )
                  {
                  var dp:ArrayCollection = (listData.owner as DataGrid).dataProvider as ArrayCollection;
                  var colIndex:int = listData.columnIndex;
                  var rowIndex:int = listData.rowIndex;
                  var colArray:Array = dp.getItemAt(colIndex) as Array;
                  text = String(colArray[rowIndex-1]);
                  }
                  else text = "";
                  }
                  }
                  }
                  ----------------------------
                  • 6. Re: dataProvider for each DataGridColumn
                    cialeen Level 1
                    Peter, I really appreciate your help.

                    But I run the application you gave, the result is the same as I said in the last post. The rowIndex will depend on the length of outter array rather than the length of each inner array.

                    In the example, [ [1,2,3,4], [10,20,30,40], [100,200,300,400] ] . The last row index is 4, but it includes the header.
                    if using "text = String(colArray[rowIndex- 1]);", the last number for each array will never be displayed.

                    : (

                    Sorry to bother you again.

                    Thanks a million.
                    • 7. Re: dataProvider for each DataGridColumn
                      peterent Level 2
                      That code works perfectly well for me. I don't see why it should be a problem. But even if it is a problem, there's enough information there for you to debug this and get it to do what you want.
                      • 8. Re: dataProvider for each DataGridColumn
                        cialeen Level 1
                        Peter, thanks for your patience.

                        If I just paste and run the application you give, the result showed in the dataGrid is:

                        Column1 Column2 Column3
                        1 10 100
                        2 20 200
                        3 30 300

                        They all missed the fourth data because the length of outter array is 3.

                        Anyway, thank you very much again. I will search more information and try to figure that out.

                        Regards
                        • 9. dataProvider for each DataGridColumn
                          cialeen Level 1
                          Hello all,

                          I am sick of this problem and I've never got what I want. I just want to set different data providers for each column. All I did is to give a property and get value from dataProvider[this.listData.rowIndex].

                          It will show the first 10 data for each column correctly if my dataGrid only can show 10 rows at one time. But, the rowIndex will change from 1to 10 all the time ( I got more than 100 rows), so no matter I scroll down or up, the dataGrid will show the first 10 data forever( actually, when you scroll down or up, the dataGrid will show a same data for 10 rows for a column, it will change to the first 10 data after you move over with all rows. How weird is that?).

                          I tried the example which Peter gave in the post, the result is the same as I got above.
                          Does someone have any idea? How can I get the real row index not the rendering row index?
                          • 10. Re: dataProvider for each DataGridColumn
                            peterent Level 2
                            I went back over the example and you are correct - I guess I was in too much of a hurry that day; the DataGrid is going to show the number of rows as there are elements in the dataProvider (3 in the example, but there should be 4).

                            You have a couple of choices, may be someone else can think of some more:
                            1. Go through your data and construct an ArrayCollection which does reflect what you want but in a form the DataGrid will use. It should not be too hard to do a matrix transofmation. There is a flash.geom.Matrix class but I don't know if it has any function you can use - but check it out; can't hurt to look.

                            2. Write your own dataProvider, extending either ArrayCollection or or the base Collection class. You have a unique data set and it is not unreasonable to write a custom data provider. This would be the more elegant of the two choices.

                            I'm sorry I didn't see the fault of my logic sooner.