5 Replies Latest reply on Oct 27, 2008 6:14 AM by Newsgroup_User

    custom renderer in datagrid, needs to know if data changed

    happybrowndog
      Hi,
      I am hoping someone can help me out please.
      I have a datagrid that uses a custom renderer that is subclassed from a TextInput. When the user changes the data in a cell, I need to color the cell. This is so that on a potentially big grid, the user can easily see which cells he has made changes to.

      It is easy enough to set the color of the itemrenderer by using setStyle() inside the overridden set data() method of the custom renderer, but that is only a fraction of the solution. Since Flex instantiates and destroys custom renderers at will depending on if it is scrolled into view by the datagrid, keeping the state of whether a value has changed inside the custom rendererer is not an option.

      So the only choice I have is to call back from the custom renderer into the container that hosts the datagrid. As the itemEditEnd event is handled in that container, a list of cells that have had their data changed can be stored. The custom renderer then needs to call back into the container with its cell coordinates and ask if the data has changed, and if it has, set its color.

      How can a custom renderer know its cell position as x,y coordinates? I see that TextInput implements IListItemRenderer interface and has properties x and y, but putting a trace on these gives me nonsensical numbers that seem to have no relation to the cell coordinates.

      The other thing I need to do is have the custom renderer call back into the container that hosts the datagrid to know whether data has changed. Is outerDocument the reference? Or is there a proper way of doing this?

      Thanks for the help you can give.
        • 1. Re: custom renderer in datagrid, needs to know if data changed
          Level 7

          "happybrowndog" <webforumsuser@macromedia.com> wrote in message
          news:ge36bo$k24$1@forums.macromedia.com...
          > Hi,
          > I am hoping someone can help me out please.
          > I have a datagrid that uses a custom renderer that is subclassed from a
          > TextInput. When the user changes the data in a cell, I need to color the
          > cell.
          > This is so that on a potentially big grid, the user can easily see which
          > cells
          > he has made changes to.
          >
          > It is easy enough to set the color of the itemrenderer by using setStyle()
          > inside the overridden set data() method of the custom renderer, but that
          > is
          > only a fraction of the solution. Since Flex instantiates and destroys
          > custom
          > renderers at will depending on if it is scrolled into view by the
          > datagrid,
          > keeping the state of whether a value has changed inside the custom
          > rendererer
          > is not an option.
          >
          > So the only choice I have is to call back from the custom renderer into
          > the
          > container that hosts the datagrid. As the itemEditEnd event is handled in
          > that
          > container, a list of cells that have had their data changed can be stored.
          > The
          > custom renderer then needs to call back into the container with its cell
          > coordinates and ask if the data has changed, and if it has, set its color.
          >
          > How can a custom renderer know its cell position as x,y coordinates? I
          > see
          > that TextInput implements IListItemRenderer interface and has properties x
          > and
          > y, but putting a trace on these gives me nonsensical numbers that seem to
          > have
          > no relation to the cell coordinates.

          I think I already said that if you implement IDropInListDataRenderer, you
          will have access to the data item (which is the entire row of data) and its
          column. This is all the information you need to accomplish what you want to
          do. You might want to look at the itemRenderer class in my
          DataGrid_withStyle example and see one way you could approach this
          http://flexdiary.blogspot.com/2008/09/extended-datagrid-with-stylefunction.html

          Also, the appropriate place to call setStyle() is in updateDisplayList().

          HTH;

          Amy


          • 2. Re: custom renderer in datagrid, needs to know if data changed
            Level 7

            "Amy Blankenship" <amySpamFilter@magnolia_pleaseNOspam_multimedia.com> wrote
            in message news:ge39qb$ob3
            > I think I already said that if you implement IDropInListDataRenderer...

            oops, looks like that post didn't go through. I'll repost.


            • 3. Re: custom renderer in datagrid, needs to know if data changed
              m_hartnett Level 3
              Here is a simplified version of how we track the cell by cell changes in a datagrid. It does require you to identify the fields that will be editable and maintaining the original data for each column in the data source.

              Our production version is much more complex than this but it will give you the idea.

              <?xml version="1.0" encoding="utf-8"?>
              <mx:Application
              xmlns:mx=" http://www.adobe.com/2006/mxml"
              layout="absolute"
              creationComplete="initApp()"
              viewSourceURL="srcview/index.html">
              <mx:Script>
              <![CDATA[
              import mx.binding.utils.BindingUtils;
              import mx.collections.ArrayCollection;
              import mx.core.Application;
              import flash.events.*;
              import mx.events.DataGridEvent;
              import mx.controls.TextInput;

              [Bindable] public var editAC : ArrayCollection = new ArrayCollection();
              [Bindable]
              public var somedata:XML = <datum><item>
              <col0>0</col0>
              <col1></col1>
              <col2></col2>
              <col3>2</col3>
              <col4></col4>
              <col5></col5>
              <col6></col6>
              <col0Orig>0</col0Orig>
              <col1Orig></col1Orig>
              <col2Orig></col2Orig>
              <col3Orig>2</col3Orig>
              <col4Orig></col4Orig>
              <col5Orig></col5Orig>
              <col6Orig></col6Orig>
              </item>
              <item>
              <col0></col0>
              <col1></col1>
              <col2></col2>
              <col3></col3>
              <col4></col4>
              <col5></col5>
              <col6></col6>
              <col0Orig></col0Orig>
              <col1Orig></col1Orig>
              <col2Orig></col2Orig>
              <col3Orig></col3Orig>
              <col4Orig></col4Orig>
              <col5Orig></col5Orig>
              <col6Orig></col6Orig>
              </item>
              <item>
              <col0></col0>
              <col1></col1>
              <col2></col2>
              <col3></col3>
              <col4></col4>
              <col5></col5>
              <col6></col6>
              <col0Orig></col0Orig>
              <col1Orig></col1Orig>
              <col2Orig></col2Orig>
              <col3Orig></col3Orig>
              <col4Orig></col4Orig>
              <col5Orig></col5Orig>
              <col6Orig></col6Orig>
              </item>
              <item>
              <col0></col0>
              <col1></col1>
              <col2></col2>
              <col3></col3>
              <col4></col4>
              <col5></col5>
              <col6></col6>
              <col0Orig></col0Orig>
              <col1Orig></col1Orig>
              <col2Orig></col2Orig>
              <col3Orig></col3Orig>
              <col4Orig></col4Orig>
              <col5Orig></col5Orig>
              <col6Orig></col6Orig>
              </item>
              <item>
              <col0></col0>
              <col1></col1>
              <col2></col2>
              <col3></col3>
              <col4></col4>
              <col5></col5>
              <col6></col6>
              <col0Orig></col0Orig>
              <col1Orig></col1Orig>
              <col2Orig></col2Orig>
              <col3Orig></col3Orig>
              <col4Orig></col4Orig>
              <col5Orig></col5Orig>
              <col6Orig></col6Orig>
              </item>
              <item>
              <col0></col0>
              <col1></col1>
              <col2></col2>
              <col3></col3>
              <col4></col4>
              <col5></col5>
              <col6></col6>
              <col0Orig></col0Orig>
              <col1Orig></col1Orig>
              <col2Orig></col2Orig>
              <col3Orig></col3Orig>
              <col4Orig></col4Orig>
              <col5Orig></col5Orig>
              <col6Orig></col6Orig>
              </item>
              </datum>
              ;

              private function initApp():void {
              var dgcols:Array = grid1.columns;
              for (var i:int=1;i<12;i++) {
              var dgc:DataGridColumn = new DataGridColumn();
              if(i < 6) {
              dgc.width=60;
              dgc.editable=true;
              dgc.dataField="col" + i.toString() ;
              dgc.rendererIsEditor=true;
              var ir :DGItemRenderer = new DGItemRenderer();
              dgc.itemRenderer = ir;
              }
              else {
              dgc.width=0;
              dgc.visible = false
              dgc.dataField="col" + i.toString() + "orig" ;
              }
              dgcols.push(dgc);
              }

              grid1.columns = dgcols;
              }




              ]]>
              </mx:Script>

              <mx:DataGrid x="31" y="27" width="200" height="150"
              id="grid1" dataProvider="{somedata.children()}"
              horizontalScrollPolicy="on" rowHeight="25" editable="true">
              <mx:columns>
              <mx:DataGridColumn headerText="Column 0"
              dataField="col0" width="30" editable="false"/>
              </mx:columns>
              </mx:DataGrid>

              </mx:Application>

              <?xml version="1.0" encoding="utf-8"?>
              <mx:TextInput xmlns:mx=" http://www.adobe.com/2006/mxml"
              creationComplete="init()"
              implements="mx.controls.listClasses.IDropInListItemRenderer, mx.core.IFactory"
              >
              <mx:Script>
              <![CDATA[
              import mx.collections.ArrayCollection;
              import mx.controls.dataGridClasses.DataGridListData;

              public function newInstance():* {
              var ir : DGItemRenderer = new DGItemRenderer();
              return ir;
              }

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

              if (value != null) {
              var colName : String= DataGridListData(listData).dataField;
              var valOrig : String = data[colName + "Orig"];
              var val : String = data[colName];

              if(valOrig != val)
              this.setStyle("backgroundColor",0xA7FF3F);
              else
              this.setStyle("backgroundColor", "#ffffff");

              }


              }

              ]]>
              </mx:Script>

              </mx:TextInput>

              • 4. Re: custom renderer in datagrid, needs to know if data changed
                happybrowndog Level 1
                mhartnett, thank you! Your solution would indeed work, and thanks for adapting it to my previous example.

                By the time I got to this post, I had answered my own question. For the benefit of the community, here is my approach:

                - the itemEditEnd event handler called "setCellColor":
                private function setCellColor(event:DataGridEvent):void {
                var colnum:int = event.columnIndex;
                var rownum:int = event.rowIndex;
                var newVal:String = DGInputCell(event.currentTarget.itemEditorInstance).text;
                newVal = StringUtil.trim(newVal);
                if (newVal!="") {
                somedata.item[rownum]["col" + colnum.toString()].@edited="1";
                }
                }
                where DGInputCell is the custom itemrenderer.

                - the set data() method of DGInputCell, which subclasses TextInput:

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

                if (xitem != null)
                {
                var edited:String = xitem[DataGridListData(listData).dataField].@edited;
                trace("edited=" + edited);
                if (edited=="1") {
                this.setStyle("backgroundColor",0xA7FF3F);
                } else {
                this.setStyle("backgroundColor",0xFFFFFF);
                }
                }
                }

                Description of solution:
                The itemEditEnd event handler adds an attribute to the XML dataprovider to signify that it has been "edited". The custom itemrenderer's set data() method traps for that attribute. Notice that if the attribute does not exist, it does not cause a fault - but that's a function of the E4X grammar I suppose.

                Lesson learned:
                In this case, there was no need to call from the custom renderer back into the container of the datagrid to determine the state of the source data that is the data provider, as had been in my original question. Instead, augment the data provider's source data as I had done (and also as mhartnett had done) and work within the Flex framework's idea that the itemrenderer is driven from data.

                I'd still like to know how to call from the itemrenderer instance back into the container holding the datagrid though?
                • 5. Re: custom renderer in datagrid, needs to know if data changed
                  Level 7

                  "happybrowndog" <webforumsuser@macromedia.com> wrote in message
                  news:ge3fvm$2cg$1@forums.macromedia.com...
                  > mhartnett, thank you! Your solution would indeed work, and thanks for
                  > adapting
                  > it to my previous example.
                  >
                  > By the time I got to this post, I had answered my own question. For the
                  > benefit of the community, here is my approach:
                  ...
                  > I'd still like to know how to call from the itemrenderer instance back
                  > into
                  > the container holding the datagrid though?

                  I guess this is the disadvantage of starting multiple threads. You don't
                  necessarily read all replies to all of your threads. The monkey patch link
                  I posted answers that question.