views:

463

answers:

3

When you have two of the same records in your data providor (i.e. the same object twice in an array) then the datagrid only allow you to select one of them, see the example below - you can only select the last "Moo".

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
 <mx:Script>
  <![CDATA[
   private var sourceData:Array = [];

   private function init():void {
    var a:Object = {Title:"Moo"};
    var b:Object = {Title:"Goo"};
    var c:Object = {Title:"Foo"};
    sourceData.push(a,b,c,a); //<-- "a" inserted twice
    dg.dataProvider = sourceData;
   }
  ]]>
 </mx:Script>

 <mx:DataGrid id="dg"/>
</mx:WindowedApplication>

I'm pretty sure this is becuase of the selectedItem property - i.e. if you have two of the same objects in your dataprovidor and you select on its only going to select the first one it finds.

Not sure the best way to resolve this. I could make sure each object is unique by cloning them all. But then I will have to make sure that updates on the 'a' object are propagated to all the "clones" of "a".

Anyone have any suggestions or ideas?

A: 

Why do you need to put the same Object into your data grid twice? The problem is not that you have two objects with the same title but that you are using the same object reference twice. You can have two objects with all the same values for every property in your data grid and you will be able to select all of them as in the code below

   var a:Object={Title: "Moo"};
   var b:Object={Title: "Goo"};
   var c:Object={Title: "Foo"};
   var d:Object={Title: "Moo"};
   sourceData.push(a, b, c, d);

If you cannot be sure that the source data doesn't contain duplicate object references then you can remove them by doing something like the following;

   var ac:ArrayCollection=new ArrayCollection();
   for (var i:int=0; i < sourceData.length; i++)
   {
    var obj:Object=sourceData[i];
    if (!ac.contains(obj))
    {
     ac.addItem(obj);
    }
   }
   dg.dataProvider=ac;
Phil C
A: 

Thanks Phil,

The main issue for me is that the data in this grid comes from a master copy of data and the use case is to allow the user to add the same item from the master copy into this grid multiple times.

So the only way i can ensure a unique reference is to clone the object. But then updates to the master copy will not flow down to the clones.

In the end i started using binding now which actually works quite well, but i'm not sure how it will perform with much larger datasets.

package
{
    import mx.binding.utils.BindingUtils;

    [Bindable]
    public class TestVO
    {
     public var a:int = 0;
     public var b:int = 0;
     public var c:int = 0;

     public function TestVO()
     {
     }

     public function clone():TestVO {
      var t:TestVO = new TestVO();
      t.a = BindingUtils.bindProperty(t,"a",this,"a").getValue() as int;
      t.b = BindingUtils.bindProperty(t,"b",this,"b").getValue() as int;
      t.c = BindingUtils.bindProperty(t,"c",this,"c").getValue() as int;
      return t;
     }
    }
}
jawache
A: 

I recently ran into the same problem. Not sure if my solution was better or worse. I thought about using binding, but the way our application was structured, I wanted to be able to explicitly tell if an object was a clone, but still be able to use a clone like the master object.

So I wound up with two classes, which is more of a pain to maintain, but the updates run through getters/setters instead of binding, which might scale better, since it's not waiting on the change event. Honestly, I don't know which approach scales better, it might be interesting to benchmark both approaches and see.

I feel like there should be a better way to do this using reflection or something, but I don't know...for type checking and data binding purposes, it probably still needs to be a class.

So basically I wound up with something like this:

package
{
    import flash.events.EventDispatcher;

    [Bindable]
    public class MasterObject extends EventDispatcher
    {
        public var a:int = 0;
        public var b:int = 0;
        public var c:int = 0;

        public function MasterObject()
        {
            super();
        }

        public function clone():MasterObject
        {
            return new CloneObject(this);
        }
    }
}

package
{
    [Bindable]
    public class CloneObject extends MasterObject
    {
        private var parent:MasterObject;

        public function CloneObject(parent:MasterObject)
        {
            super();
            this.parent = parent;
        }

        override public function get a():int
        {
            return parent.a;
        }
        override public function set a(value:int):void
        {
            parent.a = value;
        }

        override public function get b():int
        {
            return parent.b;
        }
        override public function set b(value:int):void
        {
            parent.b = value;
        }

        override public function get c():int
        {
            return parent.c;
        }
        override public function set c(value:int):void
        {
            parent.c = value;
        }
    }
}
krichard