7 Replies Latest reply on Oct 26, 2008 7:47 PM by Newsgroup_User

    Datagrid horizontal scroll messes with itemRenderer

    happybrowndog
      I've been having this issue for some time, and have tried many many different ways to solve it including writing a custom itemrenderer, but still the problem exists in one form or another.

      So I've created a very very simple program to illustrate the problem and you can see it here: http://70.68.180.48/flex1/testapp1.html . The source is viewable.

      This program is supposed to color the cell green when you put in a certain value into the datagrid. If you put in the value "X", the cell should turn green. However, if you scroll the datagrid to the right, the rendering gets confused so that other cells turn green.

      I think this is a problem with the datagrid itself. Please check this out and let me know what you think or if it can be fixed in the code.
        • 1. Re: Datagrid horizontal scroll messes with itemRenderer
          atta707 Level 2
          Let's go back to your item renderer instead of the logic in the item edit end.

          in your renderer try to add a else block:

          if (this.isinitialized==true) {
          var s:String = value[DataGridListData(listData).dataField];
          if (s=="N/A") {
          this.setStyle("backgroundColor",0xA7FF3F);
          }
          else {
          this.setStyle("backgroundColor", white[or the hex code]);
          }

          }
          • 2. Re: Datagrid horizontal scroll messes with itemRenderer
            Level 7

            "atta707" <webforumsuser@macromedia.com> wrote in message
            news:ge1ksg$fus$1@forums.macromedia.com...
            > Let's go back to your item renderer instead of the logic in the item edit
            > end.
            >
            > in your renderer try to add a else block:
            >
            > if (this.isinitialized==true) {
            > var s:String =
            > value[DataGridListData(listData).dataField];
            > if (s=="N/A") {
            > this.setStyle("backgroundColor",0xA7FF3F);
            > }
            > else {
            > this.setStyle("backgroundColor", white[or the hex code]);
            > }

            > }
            >

            And, for more information, try Q2
            http://www.magnoliamultimedia.com/flex_examples/Amys_Flex_FAQ.pdf


            • 3. Re: Datagrid horizontal scroll messes with itemRenderer
              happybrowndog Level 1
              NO, this does not work. Go back to the demo and put in a few X's. You'll see that if you move the horizontal scroll bar, sometimes the cells with X's go back to white.

              In fact, your solution to color white does not actually address the problem with whatever the problem is with the rendering in Flex itself. Your solution is not necessary anyway, if the rendering were to work correctly.

              I uploaded your change and you can see for yourself.

              Please someone make a real effort to discuss this.
              • 4. Re: Datagrid horizontal scroll messes with itemRenderer
                happybrowndog Level 1
                **** UPDATE ****

                The solution proposed by atta707 would work, if this was done in the overriden set() method of a custom itemrenderer. The reason why all states have to be checked and style set for it is because the itemrenderer is instantiated as it comes into view when the scrollbar moves the cell into view, as discussed here: http://www.adobe.com/devnet/flex/articles/itemrenderers_pt1.html?devcon=f1

                I apologize for not giving a better example of what I was ultimately trying to achieve, but it was under the assumption that the particular instance of an itemrenderer for a column/row coordinate is never "destroyed" once created.

                However, the approach that Flex developers took makes addressing my actual problem difficult. So I will state it here for the first time: What I am trying to achieve is to show the user that the user has changed the value (to any value, not just a particular value) in the cell - for the purpose of allowing the user to recognize he has made changes to a particular cell in a potentially huge grid. This is the reason why I put in logic to the itemEditEnd event handler and attempted to access the itemrenderer from "outside". However, as stated by Peter Ent in http://www.adobe.com/devnet/flex/articles/itemrenderers_pt1.html?devcon=f1, "....One thing many people try to do is access an itemRenderer from outside of the list. For example, you might want to make the cell in the fourth column of the fifth row in a DataGrid turn green because you've just received new data from the server. Getting that itemRenderer instance and modifying it externally would be a huge breech of the Flex framework and component model."

                To address my problem, I will need to track which cells (by row and column coordinates) have had changes made to them in the container holding the datagrid, and this would be done in the itemEditEnd event handler. That's simple. Now when the itemrenderer is instantiated, it would need to ask the container holding the datagrid if this is a cell that has been edited by passing along which cell it is referring to (by row and column coordinates). As itemrenderers are instantiated "on the fly" by a classfactory and destroyed when they come out of view, I can not see how they can be given a callback into the container holding the datagrid in an effort to know anything about itself except its data (a real weakness in the architecture, in my opinion). So here are my questions which I hope someone can answer to help me solve the problem:

                1) how can the custom itemrenderer know which row,column coordinates it belongs in?
                2) how can the custom itemrenderer call back into the container that holds the datagrid?


                • 5. Re: Datagrid horizontal scroll messes with itemRenderer
                  m_hartnett Level 3
                  atta707 is absolutely correct in his answer with a small bug that you should have found. Instead of checking for NA you should check for X and render the color you want otherwise color it another color (such as white). If you dont want to hardcode the white value in the code there are ways around that. You also don't need to check for the isInitialized, the fact that the value is not null is a good enough check. You also dont need to invalidate the list.

                  Thats what renderers do...they render each cell everytime something changes in the grid wheather it is a hScroll or VScroll or refresh of a dataprovider ... etc. The renderer works as it should. You need to understand what renderers do in more detail. You should read Amy's link.

                  As a side note, atta707 has helped a lot of people on this forum and the tone of your last post was preaty disrespectful. If you want people to help you, you need to work with them.

                  Here is some working code with the small but fix and using the renderer.

                  <?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: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:Script>
                  <![CDATA[
                  import mx.core.Application;
                  import flash.events.*;
                  import mx.events.DataGridEvent;
                  import mx.controls.TextInput;

                  [Bindable]
                  var somedata:XML = <datum><item>
                  <col0>0</col0>
                  <col1></col1>
                  <col2></col2>
                  <col3>2</col3>
                  <col4></col4>
                  <col5></col5>
                  <col6></col6>
                  </item>
                  <item>
                  <col0>1</col0>
                  <col1></col1>
                  <col2></col2>
                  <col3></col3>
                  <col4></col4>
                  <col5>3</col5>
                  <col6></col6>
                  </item>
                  <item>
                  <col0>2</col0>
                  <col1></col1>
                  <col2></col2>
                  <col3></col3>
                  <col4></col4>
                  <col5></col5>
                  <col6></col6>
                  </item>
                  <item>
                  <col0>3</col0>
                  <col1></col1>
                  <col2></col2>
                  <col3></col3>
                  <col4></col4>
                  <col5></col5>
                  <col6></col6>
                  </item>
                  <item>
                  <col0>4</col0>
                  <col1></col1>
                  <col2></col2>
                  <col3></col3>
                  <col4></col4>
                  <col5></col5>
                  <col6></col6>
                  </item>
                  <item>
                  <col0>5</col0>
                  <col1></col1>
                  <col2></col2>
                  <col3></col3>
                  <col4></col4>
                  <col5></col5>
                  <col6></col6>
                  </item>
                  </datum>
                  ;

                  private function initApp():void {
                  var dgcols:Array = grid1.columns;
                  for (var i:int=1;i<6;i++) {
                  var dgc:DataGridColumn = new DataGridColumn();
                  dgc.width=60;
                  dgc.editable=true;
                  dgc.dataField="col" + i.toString() ;
                  dgc.rendererIsEditor=true;
                  dgc.itemRenderer = new ClassFactory(DGInputCell);
                  dgcols.push(dgc);
                  }
                  grid1.columns = dgcols;
                  }


                  ]]>
                  </mx:Script>
                  </mx:Application>

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


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

                  if (value != null) {

                  var s:String = value[DataGridListData(listData).dataField];
                  if (s=="X") {
                  this.setStyle("backgroundColor",0xA7FF3F);
                  }
                  else {
                  this.setStyle("backgroundColor", "#ffffff");
                  }
                  }


                  }

                  ]]>
                  </mx:Script>

                  </mx:TextInput>
                  • 6. Re: Datagrid horizontal scroll messes with itemRenderer
                    happybrowndog Level 1
                    mhartnett, you really ought to have read my last post before you wrote what you did. You would then realize I meant atta707 no disrespect, and you might also realize that the tone you interpreted wasn't there.

                    However, the issue still remains in that the instantiation of itemrenderers the way Flex does it is making it difficult to solve the problem. Please read my last post and you will see what I mean.
                    • 7. Re: Datagrid horizontal scroll messes with itemRenderer
                      Level 7

                      "happybrowndog" <webforumsuser@macromedia.com> wrote in message
                      news:ge2lpp$sju$1@forums.macromedia.com...
                      > **** UPDATE ****
                      >
                      > The solution proposed by atta707 would work, if this was done in the
                      > overriden
                      > set() method of a custom itemrenderer. The reason why all states have to
                      > be
                      > checked and style set for it is because the itemrenderer is instantiated
                      > as it
                      > comes into view when the scrollbar moves the cell into view, as discussed
                      > here:
                      > http://www.adobe.com/devnet/flex/articles/itemrenderers_pt1.html?devcon=f1
                      >
                      > I apologize for not giving a better example of what I was ultimately
                      > trying to
                      > achieve, but it was under the assumption that the particular instance of
                      > an
                      > itemrenderer for a column/row coordinate is never "destroyed" once
                      > created.
                      >
                      > However, the approach that Flex developers took makes addressing my actual
                      > problem difficult. So I will state it here for the first time: What I am
                      > trying to achieve is to show the user that the user has changed the value
                      > (to
                      > any value, not just a particular value) in the cell - for the purpose of
                      > allowing the user to recognize he has made changes to a particular cell in
                      > a
                      > potentially huge grid. This is the reason why I put in logic to the
                      > itemEditEnd event handler and attempted to access the itemrenderer from
                      > "outside". However, as stated by Peter Ent in
                      > http://www.adobe.com/devnet/flex/articles/itemrenderers_pt1.html?devcon=f1,
                      > "....One thing many people try to do is access an itemRenderer from
                      > outside of
                      > the list. For example, you might want to make the cell in the fourth
                      > column of
                      > the fifth row in a DataGrid turn green because you've just received new
                      > data
                      > from the server. Getting that itemRenderer instance and modifying it
                      > externally
                      > would be a huge breech of the Flex framework and component model."
                      >
                      > To address my problem, I will need to track which cells (by row and column
                      > coordinates) have had changes made to them in the container holding the
                      > datagrid, and this would be done in the itemEditEnd event handler. That's
                      > simple. Now when the itemrenderer is instantiated, it would need to ask
                      > the
                      > container holding the datagrid if this is a cell that has been edited by
                      > passing along which cell it is referring to (by row and column
                      > coordinates).
                      > As itemrenderers are instantiated "on the fly" by a classfactory and
                      > destroyed
                      > when they come out of view, I can not see how they can be given a callback
                      > into
                      > the container holding the datagrid in an effort to know anything about
                      > itself
                      > except its data (a real weakness in the architecture, in my opinion). So
                      > here
                      > are my questions which I hope someone can answer to help me solve the
                      > problem:
                      >
                      > 1) how can the custom itemrenderer know which row,column coordinates it
                      > belongs in?

                      If you implement IDropInListItemRenderer, you will have access to listData,
                      which contains this information.

                      > 2) how can the custom itemrenderer call back into the container that holds
                      > the
                      > datagrid?

                      http://blog.flexmonkeypatches.com/2007/09/10/flex-tilelist-with-togglebuttons/

                      Let me explain what's going on here. The Flex developers put things in
                      particular places in the code so that the same segment of code doesn't run
                      over and over unnecessarily.

                      When you override set data(), you should then call invalidateProperties(),
                      which says that when the player enters the next frame, commitProperties
                      should run. commitProperties is where you should do all of your setting of
                      properties based on what happened in the set data(). The reason for this is
                      that the objects you may be trying to set properties on may not exist when
                      the first call to set data() happens. So, what you're doing is saying "I
                      know that the data has changed, so at the appropriate moment I want to make
                      changes in properties based on that." That doesn't sound very logical, but
                      if you have several properties being set, say listData _and_ data, then it
                      starts to make more sense... Even ignoring the fact that the objects may not
                      exist at the first call to set data(), if you do some things based on what
                      happens in set data() and then undo them based on what happens in set
                      listData(), how much sense does that make?

                      Instead, you wait until all property values are known, and then override
                      commitProperties(), so that when that is called you can do whatever you need
                      to do in the full knowledge of _all_ the variables that you need set before
                      you do anything based on what those values are. And if that were all that
                      were going on, you could go ahead and do your layout there, and that is
                      where the engineers would have put the layout code for the component.

                      But...there are already things that are being set in the
                      super.commitProperties(), and the layout code needs to be able to react to
                      all the properties that are being set there _and_ in your code--and here's
                      the important part--you shouldn't have to know what's going on in the super
                      method so you don't disrupt it with anything in code you put in
                      commitProperties(), either before or after the super.

                      So that's where invalidateDisplayList() and updateDisplayList() come in. So
                      if the changes you make in commitProperties() affect the size, color, or any
                      other visual property you want to set yourself, you should then override
                      updateDisplayList() and do those changes there.

                      For similar reasons, if you change anything in commitProperties (like the
                      dataProvider of a subcomponent) or updateProperties() that changes the size,
                      you need to update measure() so that the parent component can make
                      intelligent choices about how much space to allocate.

                      Hope this clarifies.

                      -Amy