Tuesday, January 18, 2011

AS3 DataGrid component: Part 13

Today we'll learn how to put CheckBox components in a DataGrid using an AS3 custom CellRenderer class.

Here's how it is going to look like:


As you can see, I took away the headers. I did that by setting the showHeaders property to false. Here's the full code in the .fla file:

import fl.data.DataProvider;
import fl.controls.dataGridClasses.DataGridColumn;

myGrid.move(10,10);
myGrid.setSize(170, 180);

var col_img:DataGridColumn = new DataGridColumn("Available");
myGrid.addColumn(col_img);
col_img.cellRenderer = loaderCellRenderer;
loaderCellRenderer._stage = this;
col_img.width = 24;

var col_desc:DataGridColumn = new DataGridColumn("Name");
myGrid.addColumn(col_desc);

var textFormat1:TextFormat = new TextFormat();
textFormat1.size = 16;
textFormat1.color = 0x333333;
textFormat1.bold = true;
textFormat1.font = "Arial";

myGrid.setStyle("headerTextFormat", textFormat1);

var textFormat2:TextFormat = new TextFormat();
textFormat2.size = 10;
textFormat2.font = "Verdana";

myGrid.setRendererStyle("textFormat", textFormat2);

var myData:Array = [{Name: "John", Available: true},
{Name: "Ralph", Available: true},
{Name: "Ted", Available: false},
{Name: "Bob", Available: true},
{Name: "Jack", Available: false},
{Name: "Claire", Available: true},
{Name: "Michael", Available: false},
{Name: "Daniel", Available: true}
];

myGrid.dataProvider = new DataProvider(myData);
myGrid.rowHeight = 26;

myGrid.showHeaders = false;

As you can see, the CheckBox data is stored in the array under the Available parameter.

Now, the loaderCellRenderer.as class. Once again, we work our way around by doing a lot of stuff that noone probably wanted us to do. Let's start!

Here is the full class code:

package 
{

import fl.controls.CheckBox;
import flash.events.MouseEvent;
import fl.controls.listClasses.ListData;
import fl.controls.listClasses.ICellRenderer;

public class loaderCellRenderer extends CheckBox implements ICellRenderer
{
protected var _data:Object;
protected var _listData:ListData;
public static var _stage;

public function loaderCellRenderer()
{
super();
label = "";
addEventListener(MouseEvent.CLICK, onClick);
}

public function onClick(MouseEvent):void
{
_selected = ! _selected;
_data.Available = _selected;
}

public function get data():Object
{
return _data;
}

public function set data(value:Object):void
{
_data = value;
_selected = _data.Available;
}

public function get listData():ListData
{
return _listData;
}

public function set listData(value:ListData):void
{
_listData = value;
}

override public function get selected():Boolean
{
return _selected;
}

override public function set selected(value:Boolean):void
{
}

}
}

Now let me explain.

If you read the code from the start, you'll probably notice that we are not defining the _selected variable. That is because it already exists in the CheckBox class.

For some reason, if we simply make the class extend CheckBox and leave it like that (with the needed getters setters), the code will work, however, it acts very weird. We won't be able to select more than 1 CheckBox in the whole datagrid, and if we do select one, there's no way to unselect it rather than select another one. Also, the checkbox is selected if you click anywhere in the row its in.

To fix this, you'll need to override the get selected and set selected functions. Empty out the set selected one and leave the getter with one simple return. Now, we've cleared the stuff that doesn't work. Let's add our own code to make it functional!

Add a click listener on the checkbox and add the line that manage the toggling feature. We also need to get and set data from the main array.

Hope this helped!

Thanks for reading!

Related:

AS3 DataGrid component: Part 1
AS3 DataGrid component: Part 2
AS3 DataGrid component: Part 3
AS3 DataGrid component: Part 4
AS3 DataGrid component: Part 5
AS3 DataGrid component: Part 6
AS3 DataGrid component: Part 7
AS3 DataGrid component: Part 8
AS3 DataGrid component: Part 9
AS3 DataGrid component: Part 10
AS3 DataGrid component: Part 11
AS3 DataGrid component: Part 12
AS3 DataGrid component: Part 14
AS3 DataGrid component: Part 15
AS3 DataGrid component: Part 16

26 comments:

dwalvin said...

Kirill,
I don't know what to say other than "THANK YOU"! I have been trying to add a checkbox to my datagrid for the last 8 hours and NOTHING was working right -- including samples from all the usual AS3 forums sites (and most importantly) Adobe's. Your sample code WORKS. I could kiss you. -Dave

Kirill Poletaev said...

Haha, thanks! It took me quite some time and nerves to finally write the working code, because it is kind of frustrating when you're trying to make something work the way it wasn't meant to :P

fvasquez said...

Hi... Thank you very much!!!... I have a question, How to scroll the datagrid to know the checkboxs selected?

Kirill Poletaev said...

What do you mean? Can you rephrase your question?

fvasquez said...

Hi, I did the example in AIR and Flash, but the problem is when I move the scroll, change the selected checkbox. I uploaded the example to the following url:
http://www.megaupload.com/?d=OHITQSDP

fvasquez said...

Hi, the problem is, the datagrid does not refresh the checkboxes to move the scroll. I hope you can help me, thanks.

Kirill Poletaev said...

It seems that it works fine if the column with the checkboxes is the first column. I don't know why it doesn't work that way when its not in the first column, I will tell you if I find the solution...

fvasquez said...

Ok, thanks

Matt said...

would it be possible to have multiple check boxes in a single row? when i do it this way, if i select one check box, all the check boxes in that row get checked

Matt said...

ive even tried implementing another CustomCellRenderer and setting the onclick function to only set _selected = !_selected and _Data.Available = _selected IF the mouse event x coordinate was inside the proper column... this does not work and still checks off the boxes in both columns every time

Matt said...

i also put traces in each of the functions, and it seems that altho i have 2 seperate custom cell renderers, the data source is the same for any cell using that custom renderer (mayb every single cell of the datagrid??) because when i check off the box in column 1, the set data method is never called on column 2, but it still changes from not selected to selected

Matt said...

I figured out my problem, giving the 2nd custom cell renderer _data.Available a dif name like _data.AvailableColTwo works.

Mei Ling said...
This comment has been removed by the author.
That Guy! said...

This is very nice and helpful. I've implemented it without any problem, and also use the color rendering class to indicate if a row shouldn't be edited. But how do you (based on data in the data provider) turn off editing for an entire row?

Kirill Poletaev said...

Well, you can add some conditional logic to the onClick() function which will check if the data should be edited on click.

Bongio said...

Hi Kirill,

and thank you very much for this knowledge sharing. I found it extremely useful and saved your link in my bookmarks.

I would like to ask you a thing. May it be possible to add a border for cells where I draw the checkboxes? Where should I add the code?

Thanks in advance, and congratulations again.

Kirill Poletaev said...

Well, perhaps the most simple way would be editing the checkbox graphic itself and draw the box there, but that might not work in some situations...

You could also try drawing the box using code. I haven't tried myself, but you could try writing that code in the constructor (loaderCellRenderer()).

Rui said...

can you help me,how i know the selected checkbox or not? what variable that i should access..
i'm newbie in as3.
thx..

rosquito said...

Great tute, much appreciated!
How would you go about clearing the selected checkbox in the case that the user wanted to start over?

dev@climbapps.com said...

Man, you are so awesome and I have learned so much from a ton of your work!

I was wondering if there was a way to merge this into my existing xml data; I keep getting a cell renderer error even after I put the class in the correct location etc.

Kirill Poletaev said...

It should work as you say, but if it doesn't read XML properly - try parsing it and turning it into an array, and then feeding it to the data grid.

dev@climbapps.com said...

I am getting very close but can't make the checkbox show up...have CellRenderer.as established, etc; not getting errors at this point (!) but just can't see box...

Would be willing to pay you for help, I know I am a pain in the butt. My email is climbapps@gmail.com


import fl.controls.dataGridClasses.DataGridColumn;
import fl.data.DataProvider;
import flash.net.*;
import flash.events.*;

var request:URLRequest = new URLRequest("team.xml");
var loader:URLLoader = new URLLoader;


loader.load(request);
loader.addEventListener(Event.COMPLETE, loaderCompleteHandler);

function loaderCompleteHandler(event:Event):void {

var teamXML:XML = new XML(loader.data);



var col_img:DataGridColumn = new DataGridColumn("Available");
aDg.addColumn(col_img);
col_img.width = 12;




var nameCol:DataGridColumn = new DataGridColumn("name");
nameCol.headerText = "Rating";
nameCol.width = 15;
nameCol.sortOptions = Array.NUMERIC


var avgCol:DataGridColumn = new DataGridColumn("avg");
avgCol.headerText = "Route Name";
avgCol.width = 45;
var typeCol:DataGridColumn = new DataGridColumn("type");
typeCol.headerText = "Type";
typeCol.width = 15;


var locCol:DataGridColumn = new DataGridColumn("loc");
locCol.headerText = "Location";
locCol.width = 30;
var starCol:DataGridColumn = new DataGridColumn("star");
starCol.headerText = "Stars";
starCol.width = 12;
var numbCol:DataGridColumn = new DataGridColumn("numb");
numbCol.headerText = "Route #";
numbCol.width = 15;
numbCol.sortOptions =Array.NUMERIC


var myDP:DataProvider = new DataProvider(teamXML);

aDg.columns = [col_img, nameCol, avgCol, typeCol, locCol, starCol, numbCol];
aDg.setSize(440,680);
aDg.dataProvider = myDP;








var textFormat1:TextFormat = new TextFormat();
textFormat1.size = 12;
textFormat1.color = 0x0000CC;
textFormat1.bold = true;
textFormat1.font = "Arial"

aDg.setStyle("headerTextFormat", textFormat1);


var textFormat2:TextFormat = new TextFormat();
textFormat2.size = 14;
textFormat2.bold = true;
textFormat2.font = "Arial"

aDg.setRendererStyle("textFormat", textFormat2);


}

Anonymous said...

You have to add one Checkbox element from components menu to your library. After this step, you'll see the checkbox in the desidered column when you run the project!!!

Hope this helps you!

Bye

Barak Harel said...

Just a quick heads up - adding:

override protected function draw():void
{
this.invalidate();
super.draw();
}

Fixes all of the redrawing issues like scrolling, but it has it's price on the cpu...

Paul Kamma said...

Hi.

How is it possible taht I recieve an Event outside of the cellRenderer that a Checkbox was changed?
I tryed various methods to attach an Eventlistener out of the DataGrid but with no success.. :/

- Paul

Kirill Poletaev said...

You could use the onClick function inside the cellrenderer, which is already a listener for the click event. If you want to add a handler function from the root file, create a handler function there, then pass a reference to that function as an attribute to items in the dataprovider, and then call that function from the cell renderer's onClick method. Hope this helps.

Sorry for extremely late reply, for some reason I haven't seen your comment in my feed...

Post a Comment