Affichage des articles dont le libellé est as3. Afficher tous les articles
Affichage des articles dont le libellé est as3. Afficher tous les articles

vendredi 19 juin 2009

How to customize a cell in a datagrid with AS3 : Part 2

In the previous ticket, How to customize a cell in a datagrid with AS3, I explained how to change the background color of a cell. Now, let see how to add a datagrid that contains various components like checkboxes and buttons. I will take the same data model:
Name$
Apple0.5
Bread1.99
Asparagus3.0
Banana0.8
But in addition of those two columns, I want to add a checkbox and , let's say, an input field. And the checkbox represents which groceries the user want to buy, with the input field indicating the quantity. How to do that? This can be done in 2 steps and 2 classes. And the UILoader component. Let's start again with the cell renderer. Instead of extending the cellrenderer, it will this time be extending UILoader. This will allow to put any kind of component or movieclip inside the cell, which makes it reusable. However, because I could only get the whole row values but not the current cell value, I have to create a cell renderer for each column. But there is only one field changing from one cell renderer to another. So I post the template there:
package {
 import fl.containers.UILoader;
 import fl.controls.listClasses.ICellRenderer;
 import fl.controls.listClasses.ListData;
 import fl.core.InvalidationType;

 public class LoaderCellRenderer extends UILoader implements ICellRenderer {
     protected var _data:Object;
     protected var _listData:ListData;
     protected var _selected:Boolean;
  
     public function LoaderCellRenderer():void {
         super();
     }
  
     public function get data():Object {
         return _data;
     }
     public function set data(value:Object):void {
         _data = value;
         source = value.Select;
     }
  
     public function get listData():ListData {
         return _listData;
     }
     public function set listData(value:ListData):void {
         _listData = value;
         invalidate(InvalidationType.DATA);
         invalidate(InvalidationType.STATE);
     }
  
     public function get selected():Boolean {
         return _selected;
     }
     public function set selected(value:Boolean):void {
         _selected = value;
         invalidate(InvalidationType.STATE);
     }
  
     public function setMouseState(state:String):void {
     }
 }
}
In bold, you can see the name of the column which provides the movieclip to render. In this example, the cell renderer is using the content of the data of each row at the column Select. In the data, instead of storing a text or number, I'll store a checkbox object that the UILoader will use. And so is the trick. For this demo, I've also created a copy of this class for the column Quantity, which will contain the input field. In the second step, I need to configure the datagrid columns and to add data properly: To configure columns:
var SelectColumn:DataGridColumn = new DataGridColumn("Select");
SelectColumn.cellRenderer = LoaderCellRenderer;
SelectColumn.width = 25;

var QtyColumn:DataGridColumn = new DataGridColumn("Quantity");
QtyColumn.cellRenderer = LoaderCellRendererInput;

dataGrid.addColumn(SelectColumn);
dataGrid.addColumn(QtyColumn); 
dataGrid.addColumn("Name");
dataGrid.addColumn("Price");
Now the datagrid is ready. You can add data in its dataprovider like that:
var c:Checkbox=new Checkbox();
c.text="";
var input:TextInput=new TextInput();
reportDataGrid.addItem({Select:c, Quantity:input, Name:"Apple",Price:0.5}); 
I have now a row with a checkbox, a textinput and the usual textfields Apple and 0.5. It's possible to populate the list and have to look that I wanted. But it's pretty much useless if the components do nothing. Sure, with the dataprovider we know if the checkbox of a certain line is checked, or we can read the value of a specific textinput. But sometimes, we want to trigger an event from a component in one of those rows. Suppose we want to check the checkbox of a row when we enter a quantity. To do that, we have to create a class that extends the textinput, because the component needs to know wich checkbox to change and the original textinput is missing a field for that. So, I simply create a CustomTextInput that extends TextInput and that has a public var of type Checkbox by default null. Then, to add data:
var c:Checkbox=new Checkbox();
c.text="";
var input:CustomTextInput=new CustomTextInput();
input.checkbox=c;
input.addEventListener(Event.CHANGE,onQtyChange);
reportDataGrid.addItem({Select:c, Quantity:input, Name:"Apple",Price:0.5}); 


function onQtyChange(e:Event){
  var input:CustomTextInput=CustomTextInput(e.currentTarget);
  input.checkbox.selected=true;
}
And so, the checkbox of each row will be affected by the input field changes in the same row.

mardi 12 mai 2009

Thou shall not touch Lists at the Beginning

As strange as it looks like, if you try to access to the dataProvider of a List created in flash from the constructor of a class associated to a movieclip that owns the list, you'll get a bug. In fact, it seems that the dataProvider of the list object is initialized after the constructor of the parent movieclip is executed, meaning that whatever you do with the list or its dataProvider in the constructor will not be shown. Very simple example:
  1. in flash 9, create a new movieclip and add to it a List component.
  2. create a classe for the movieclip.
  3. add in the class a constructor with the following content: myList.dataProvider.addItem({label:"test",data:0});
  4. run it and expect to see a list with a line "test". But because of some kind of bug, nothing appears in the list.
However, if you do the addItem from a button click event, it works perfectly fine.

lundi 11 mai 2009

The List won't listen

Once again I'm fighting with the component List and its dataProvider. For some reason it refuses to do the simplest update when I do a removeAll() or an AddItem() if the script is executed inside a class of the parent movieclip. This just doesn't make sense.