Displaying a checkbox column in a Flex DataGrid - apache-flex

I am trying to create a custom Flex DataGrid which consists of two columns, one for label and the other for a checkbox.
The checkbox is contained within a class called CustomRenderer that inherits from GridItemRenderer and implements IFactory.
In each instance of CustomRenderer, I have a variable called number so that when a checkbox is clicked, it can pass back the number so that I know which row has been clicked.
The problem is that my CustomRenderer does not show when the program runs.
Please could someone help.
Here is my code.
MyProgram.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
initialize="initData()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.controls.CheckBox;
import mx.rpc.events.AbstractEvent;
private var ArrayItems:Array = [
{Label:'Table'},
{Label:'Chair'},
{Label:'Stool'},
{Label:'Bench'},
{Label:'Sofa'}];
[Bindable]
private var ArrayCollectionItems:ArrayCollection;
private var t:CustomRenderer;
public function initData():void
{
ArrayCollectionItems=new ArrayCollection(ArrayItems);
t = new CustomRenderer();
t.cb.addEventListener(MouseEvent.CLICK, HandleClick);
MainGrid.columns[1].itemRenderer = t.newInstance();
}
public function HandleClick(event:Event):void
{
var c:CustomRenderer = (event.currentTarget) as CustomRenderer
var RowClicked:int = c.number;
}
]]>
</fx:Script>
<s:states>
<s:State name="State1"/>
</s:states>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Panel x="99" y="14" width="379" height="268" skinClass="spark.skins.spark.PanelSkin">
<s:DataGrid id="MainGrid" x="19" y="17" width="340" height="199"
dataProvider="{ArrayCollectionItems}" requestedRowCount="4" requireSelection="false"
resizableColumns="false" selectionMode="singleRow" showDataTips="false">
<s:columns>
<s:ArrayList>
<s:GridColumn minWidth="200" dataField="Label" headerText="Label"></s:GridColumn>
<s:GridColumn minWidth="30" dataField="" headerText="">
</s:GridColumn>
</s:ArrayList>
</s:columns>
<s:typicalItem>
<fx:Object dataField1="Sample Data" dataField2="Sample Data" dataField3="Sample Data"></fx:Object>
</s:typicalItem>
</s:DataGrid>
</s:Panel>
</s:Application>
CustomRenderer.as
import mx.controls.Alert;
import mx.controls.CheckBox;
import mx.core.IFactory;
import spark.components.gridClasses.GridItemRenderer;
public class CustomRenderer extends GridItemRenderer implements IFactory
{
private static var count:int = 0;
public var number:int;
public var cb:CheckBox;
public function CustomRenderer()
{
cb = new CheckBox();
number = count++;
cb.x = 22;
cb.y = 4;
addElement(cb);
}
public function newInstance():*
{
return new CustomRenderer();
}
}

Probably that's because you pass an instance of the itemrenderer to the column (new CustomRenderer()) instead of a class (CustomRenderer). The common approach is to pass the itemrenderer class to your column and then handle the clicks / selection inside of it:
<s:GridColumn minWidth="30" headerText="" itemRenderer="com.yourpackage.CustomRenderer">
Just start typing "Customrenderer" when you assign it and you should be able to autocomplete so it writes out your full package path.
Then you catch the checkbox select events inside of the renderer and could set some custom data property (for example, data.selected). If you really need to do something outside of your Grid after the selection you might need to fire a custom event from your renderer on selection and listen for it on your datagrid.

Related

Getting flex datagrid cell value using column and row indices

i'm using flex for an application but i'm having some trouble to recover data from a datagrid that has a checkbox in one of the columns, the problem is that i can't use the function selectedIndex or selectedItem, because they are used to save the checked items.
Is there a way to recovery the datagrid informations using the column and row indices? Something like:
MyDataGrid[row][column] or MyDataProvider[row][column] or any combination of functions to allow me recovery an information using only the row and column.
Thanks
If you just want to be able to get a cell value of the data grid using row and col indices, you can extend the DataGrid component and add one simple function.
Here is a working example.
It looks something like this:
//CustomDataGrid
<?xml version="1.0" encoding="utf-8"?>
<s:DataGrid xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="400" height="300">
<fx:Script>
<![CDATA[
import spark.components.gridClasses.GridColumn;
public function getElementAt(row:int, col:int):Object
{
if (this.dataProvider.length < row + 1 || this.columns.length < col + 1)
return null;
else
return this.dataProvider.getItemAt(row)[(this.columns.getItemAt(col) as GridColumn).dataField];
}
]]>
</fx:Script>
</s:DataGrid>
Then you call it in your application like this:
//App
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600"
xmlns:dgrc="com.dgrc.*">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
[Bindable]private var collection:ArrayCollection = new ArrayCollection([
{fld01:1, fld02:"fld02", fld03:"fld13", fld04:"fld14"},
{fld01:2, fld02:"fld02", fld03:"fld23", fld04:"fld24"},
{fld01:3, fld02:"fld02", fld03:"fld33", fld04:"fld34"},
{fld01:4, fld02:"fld02", fld03:"fld43", fld04:"fld44"},
{fld01:5, fld02:"fld02", fld03:"fld53", fld04:"fld54"}
]);
protected function getElement():void
{
var obj:Object = myDG.getElementAt(nsRow.value, nsCol.value);
Alert.show(String(obj));
}
]]>
</fx:Script>
<s:VGroup x="20" y="20">
<s:HGroup>
<s:NumericStepper id="nsRow" minimum="0" value="0"/>
<s:NumericStepper id="nsCol" minimum="0" value="0"/>
<s:Button label="Get It!" click="getElement()"/>
</s:HGroup>
<dgrc:CustomDataGrid id="myDG" width="300" height="160" dataProvider="{collection}">
<dgrc:columns>
<s:ArrayList>
<s:GridColumn dataField="fld01" headerText="Field 1" width="100"/>
<s:GridColumn dataField="fld03" headerText="Field 3" width="100"/>
<s:GridColumn dataField="fld04" headerText="Field 4"/>
</s:ArrayList>
</dgrc:columns>
</dgrc:CustomDataGrid>
</s:VGroup>
</s:Application>
Be aware that the row and col indices in this example refer a cell of the data grid and not a value of the data grid provider.
If you call
getElementAt(0, 1)
you will get "fld13"
If you are using the original DataGrid control, it is better to use the itemToLabel method of DataGridColumn, because item[dataField] will cause an exception when the property value contains dot notation, such as "customer.name":
public function getElementAt(row:int, col:int):Object {
var item:Object = dataProvider.getItemAt(row)
var column:DataGridColumn = dataGrid.columns[column] as DataGridColumn;
var value:String = column.itemToLabel(item);
return value;
}
This same method is used by the framework to provide the text displayed in the grid cell. If your checkbox in the grid is driven by a boolean property, the returned value will be true or false.

Flex : How to reuse Item renderer?

I have a data grid table, the gridColumn in the dataGrid table calls a "DropDownListItemRenderer". Using the same item renderer I want to change the contents in the drop down list according to the dataField name. So for example if the dataField name is colour then the drop down will contain red,blue,green ect. If the dataField name is furits, then the drop down list will contain different fruits options.
If u look at the codes in my item Renderer. I tried to do the above by creating a bindable array collection called dropDownListData. In the override set data function I get the dataField name, Using the data field name I add items in array collection accordingly.
Although the dropdown list is filled with the correct data when u run the program. The data in the drop down list is repeated and increased every time when the dropdown list is selected.
I think I am not using the right method to do this. So can someone show me how I do this ? Pls can someone help me with this ? Pls let me know if my question is not clear I will try to rephrase it.
Thanks :)
This is my MXML file :
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import FrontEndObjects.ColourItems;
import mx.collections.ArrayCollection;
import spark.events.IndexChangeEvent;
[Bindable]
private var order:ArrayCollection = new ArrayCollection();
private function addOrder():void{
var orderItems:ColourItems = new ColourItems();
order.addItem(orderItems);
}
]]>
</fx:Script>
<s:BorderContainer x="175" y="101" width="606" height="289">
<s:DataGrid id="myDG" x="53" y="27" width="516" height="201" dataProvider="{order}"
editable="true" variableRowHeight="true">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="label1" headerText="Order #" editable="true"/>
<s:GridColumn dataField="quant" headerText="Qty" editable="true"/>
<s:GridColumn dataField="color" headerText="Color" editable="true" rendererIsEditable="true" itemRenderer="myRenderers.DropDownListItemRenderer"/>
<s:GridColumn dataField="furits" headerText="Furits" editable="true" rendererIsEditable="true" itemRenderer="myRenderers.DropDownListItemRenderer"/>
</s:ArrayList>
</s:columns >
</s:DataGrid>
<s:Button x="499" y="236" label="add" click="addOrder()" />
</s:BorderContainer>
This is my DropDownList item Renderer :
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" clipAndEnableScrolling="true">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import spark.components.gridClasses.GridColumn;
import spark.events.IndexChangeEvent;
public var cName:String;
[Bindable]
private var dropDownListData:ArrayCollection = new ArrayCollection();
protected function onCbChange(event:IndexChangeEvent):void
{
var value:String = (event.currentTarget as DropDownList).selectedItem;
data[column.dataField] = value;
}
override public function set data(value:Object):void
{
super.data = value;
cName = column.dataField;
if(cName == "color"){
dropDownListData.addItem("red");
dropDownListData.addItem("blue");
dropDownListData.addItem("green");
}
else if(cName == "furits"){
dropDownListData.addItem("banana");
dropDownListData.addItem("apple");
dropDownListData.addItem("grapes");
}
}
]]>
</fx:Script>
<s:DropDownList id="cb" width="100%" change="onCbChange(event)" requireSelection="true" dataProvider="{dropDownListData}"/>
This is my Object class :
public class ColourItems
{
public var label1:String;
public var quant:String;
public var color:String;
public var furits:String;
}
In the setData method of the itemRenderer, you should check what data already exists in the list before doing the addItem
You set target columns as editable: editable="true" rendererIsEditable="true". When you click for cell, grid calls set data(value:Object) method in itemrenderer where you add items in dropdown list more more and more. You can easy fix it as disabled editable for cell:
<s:GridColumn dataField="color" headerText="Color" editable="false" itemRenderer="myRenderers.DropDownListItemRenderer"/>
<s:GridColumn dataField="furits" headerText="Furits" editable="false" itemRenderer="myRenderers.DropDownListItemRenderer"/>

Flex Spark datagrid - disable selection of a row

I want to disable (and look disabled) some rows in a spark datagrid. I found this answer to stop selection which is great
In flex, is there a way to capture and optionally cancel a row selection event in a DataGrid?
But I want in addition to show that the particular row is not selectable. Ideally I want to have some sort of overlay but I am not sure if that is possible. My alternative solution is to change the text color to grey for the unselectable row. Looking at the datagrid renders they all seem to be column based. I looked at skinning potentially (overriding the alternating color property) but this just sets the background property and not the text color. Is this possible?
Thanks
The most basic solution is using the custom renderer and selection preventing logic you've mentioned:
DataGridRowDisabling.mxml
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:local="*">
<fx:Script>
<![CDATA[
import spark.events.GridSelectionEvent;
private function dataGrid_selectionChangingHandler(event:GridSelectionEvent):void
{
var index:int = event.selectionChange.rowIndex;
var product:Product = dataGrid.dataProvider.getItemAt(index) as Product;
if (product && !product.enabled)
event.preventDefault();
}
]]>
</fx:Script>
<s:DataGrid id="dataGrid" itemRenderer="GridItemRenderer2" selectionChanging="dataGrid_selectionChangingHandler(event)">
<s:dataProvider>
<s:ArrayCollection>
<local:Product name="iPod" price="199.99"/>
<local:Product name="iPad 3" price="499.99"/>
<local:Product name="iPad 4" price="599.99" enabled="false"/>
<local:Product name="iPad 5" price="699.99" enabled="false"/>
</s:ArrayCollection>
</s:dataProvider>
</s:DataGrid>
</s:Application>
GridItemRenderer2.mxml
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" clipAndEnableScrolling="true">
<fx:Script>
<![CDATA[
override public function prepare(hasBeenRecycled:Boolean):void
{
if (data is Product)
enabled = Product(data).enabled;
lblData.text = data[column.dataField];
}
]]>
</fx:Script>
<s:Label id="lblData" top="9" left="7"/>
</s:GridItemRenderer>
Product.as
package
{
public class Product
{
public var name:String;
public var price:Number;
public var enabled:Boolean = true;
}
}

Weird behavior of custom item renderer in Flex 4

I have a class defined like this:
public class Photo
{
public function Photo()
{
}
public var PhotoId:int;
public var Title :String;
public var Subtitle :String;
public var PhotoByteArray:ByteArray ;
public var ThumbnailByteArray:ByteArray;
public var ShowOnlyInFrontpageTop:Boolean;
public var ShowInFrontpageGroup:Boolean;
public var BackgroundColorGradientFrom:String;
public var BackgroundColorGradientTo:String;
public var IsActive :Boolean;
}
I 'm getting diverse objects of this type (Photo) from a WCF service (works fine!). These objects are stored in an ArrayCollection defined like this:
public static var GroupPhotos:ArrayCollection = new ArrayCollection();
I want to show up these photos using a s:List component, like this:
<s:List height="110" id="GroupPhotos" itemRenderer="MyGroupPhotoRenderer">
<s:layout >
<s:HorizontalLayout requestedColumnCount="3" />
</s:layout>
</s:List>
The item renderer is defined this way:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="true"
creationComplete="onItemrenderer_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
//when the item has been rendered and is ready to be shown the following method will be called
protected function onItemrenderer_creationCompleteHandler(event:FlexEvent):void
{
img.source = data.ThumbnailByteArray;
}
]]>
</fx:Script>
<s:Group id="PhotoGroup" width="297" height="110" >
<!--<s:Rect id="imgRectangle" alpha="0" width="95" height="65">
<s:stroke>
<s:LinearGradientStroke weight="{GroupBoxBorderWeight}" scaleMode="none"/>
</s:stroke>
</s:Rect>-->
<mx:Image id="img"
width="{PhotoGroup.width}" height="{PhotoGroup.height}"
/>
<s:Label id="title"
fontSize="20"
text="{data.Title}"/>
</s:Group>
</s:ItemRenderer>
The s:Label component shows up correctly whereas the mx:Image component shows up always the same image (don't know if this is the first Image or the last in the array).
What am i missing?
Thanks in advance
Ahhm!!Turns out that this my error! Above i stated that the service is running fine: guess what...it didn't! Fixing the service made the images show up correctly!
Creation complete is only called the first time your renderer is created. Since Flex reuses renderers that means it will only get called the first time. Instead of using creationComplete, override the set data method of your renderer. Unfortunately, s:ItemRenderer doesn't have a set data method, so I'd use a mx:HBox component instead.
Here's a quick example to get you started:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
override public function set data(value:Object):void
{
super.data = value;
theLabel.text = value["Title"].toString();
theImage.source = data.ThumbnailByteArray;
validateDisplayList();
}
]]>
</fx:Script>
<mx:Label id="theLabel" />
<mx:Image id="theImage" />
</mx:HBox>

Scrollbars in dropdownlist inside DataGrid itemEditor not working

I have a DropDownList inside the itemEditor of my DataGrid. There are enough items in the DropDownList to justify scrollbars. You can see the scrollbars, and the mousewheel works fine. If you move the mouse over the scrollbars, they'll change appearance fine (mouseover is working). If you click on them, the DropDownList closes as if you'd clicked elsewhere in the data grid.
There's a comment on the Adobe Forums that describe the same problem, but it is fairly old and has not been answered.
I've been experimenting with custom skins hoping to find a way to trap the mouse event, but have been unsuccessful.
FWIW, this is Flex4, as an AIR app.
Scratch.mxml (main code)
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="windowedapplication1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
[Bindable] public var dataList:ArrayCollection = new ArrayCollection();
protected function windowedapplication1_creationCompleteHandler(event:FlexEvent):void
{
var o:Object = new Object();
o.theChoice = "abc";
o.choices = new ArrayCollection();
o.choices.addItem("abc");
o.choices.addItem("def");
o.choices.addItem("ghi");
o.choices.addItem("jkl");
o.choices.addItem("mno");
o.choices.addItem("pqr");
o.choices.addItem("stu");
o.choices.addItem("vwx");
o.choices.addItem("yz ");
dataList.addItem(o);
}
protected function col2Label(item:Object, column:DataGridColumn):String {
return item.theChoice;
}
// If you use the labelFunction, then you must specify a sortCompareFunction
private function sortCol2(obj1:Object, obj2:Object):int
{
var d1:String = obj1.col2 as String;
var d2:String = obj2.col2 as String;
if(d1 < d2) {
return -1;
} else if(d1 == d2) {
return 0;
}
return 1;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<mx:DataGrid id="glGrid" top="10" left="10" right="10" bottom="10"
dataProvider="{dataList}" editable="true" >
<mx:columns>
<mx:DataGridColumn id="col2"
headerText="Column 2"
itemEditor="Renderers.ddlRenderer"
labelFunction="col2Label"
dataField="col2"
sortCompareFunction="sortCol2"/>
</mx:columns>
</mx:DataGrid>
</s:WindowedApplication>
ddlRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
focusEnabled="true">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
import spark.events.IndexChangeEvent;
[Bindable] private var myChoices : ArrayList = new ArrayList();
override public function set data(value:Object):void
{
if (value != null) {
super.data = value;
if (ddl && myChoices) {
myChoices.removeAll();
var theChoice:String = value.theChoice;
myChoices.addAll(value.choices);
var lineChoice : String;
for (var i:int = 0; i < myChoices.length; i++) {
lineChoice = myChoices.getItemAt(i) as String;
if (lineChoice == theChoice) {
ddl.selectedItem = lineChoice;
break;
}
}
}
}
}
]]>
</fx:Script>
<s:DropDownList id="ddl"
width="100%"
dataProvider="{myChoices}"/>
</s:MXDataGridItemRenderer>
To see the problem, run the code, click on "abc" to trigger the itemRenderer, click on the DropDownList to see the choices, then try clicking on the scrollbar.
I'm stumped on this one, and would greatly appreciate some help.
Thanks
Dan
I posted this to Adobe as a bug (SDK-27783, Flex SDK, Spark:DropDownList), which was just closed today. Alex Harui had a good workaround:
Workaround is to change renderer as follows:
<s:DropDownList id="ddl"
width="100%"
dataProvider="{myChoices}" open="ddl.skin['dropDown'].owner = this"/>
I tested this and it solves my problem. Hopefully this will help others as well.
I'm not sure that it's a mouseEvent that you should to trap. You can debug the framework class: DropDownController.as, put a breakpoint at the start of the systemManager_mouseDownHandler function and processFocusOut function. You can see when you clic on the dropdownlist'scrollbar that the systemManager_mouseDownHandler function doesn't call closeDropDown, closeDropDown is called by processFocusOut.
Now add a DropDownList object at the top of your application:
<s:DropDownList id="ddltop"
top="10"
left="10"
width="100%"
dataProvider="{dataList.getItemAt(0).choices}"
/>
<mx:DataGrid id="glGrid" top="50" left="10" right="10" bottom="10"
...
and debug again with the same breakpoints. Now it's the systemManager_mouseDownHandler function that call closeDropDown.
Perhaps the reason for the closure of the dropdownlist.

Resources