Flex DataGrid with row number column - apache-flex

I want to extend the DataGrid component so that there is a (read-only) column for the row number like you see in spreadsheets. I came across this article http://www.cflex.net/showFileDetails.cfm?ObjectID=735 but it depends on the data being unique for each row so that it can index into the array. If the data is not unique (like for an empty grid) it doesn't work. How can I implement that?

This worked for me:
<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.controls.AdvancedDataGrid;
private var handleDataChangedEnabled:Boolean = false;
override public function set data(value:Object):void {
super.data = value;
if (!handleDataChangedEnabled) {
addEventListener("dataChange", handleDataChanged);
}
}
public function handleDataChanged(event:Event):void {
this.text = String(listData.rowIndex + (listData.owner as AdvancedDataGrid).verticalScrollPosition + 1);
}
]]>
</mx:Script>
Of course, you would have to change AdvancedDataGrid to DataGrid.
Cheers.

ensure that the dataProvider has a unique column or property, then dont show that column/property if you dont wont to.
The key is the dataProvider

Just use this class as your itemRenderer:
RowNumColumnRenderer.as
package
{
import mx.collections.IList;
import mx.controls.AdvancedDataGrid;
import mx.controls.Label;
import mx.controls.listClasses.ListBase;
public class RowNumColumnRenderer extends Label
{
override public function set data(value:Object):void
{
super.data = value;
if (listData != null)
this.text = (AdvancedDataGrid(listData.owner).itemRendererToIndex(this) + 1).toString();
}
}
}

I was able to do this by implementing a custom itemRenderer, RowNumberRenderer.as
package com.domain
{
import mx.collections.IList;
import mx.controls.Label;
import mx.controls.listClasses.ListBase;
public class RowNumberRenderer extends Label
{
public function RowNumberRenderer()
{
super();
}
override public function set data(value:Object):void
{
super.data = value;
this.text = String(IList(ListBase(listData.owner).dataProvider).getItemIndex(data) + 1);
}
}
}

how about the following:
RendererRowIndexPlusOne.as
package
{
import mx.controls.Label;
import mx.utils.StringUtil;
import mx.utils.ObjectUtil;
public class RendererRowIndexPlusOne extends Label
{
public override function set data(item:Object):void {
super.data = item;
trace('listData.label ' + listData.label);
trace('listData.rowIndex ' + listData.rowIndex);
trace('listData.columnIndex ' + listData.columnIndex);
trace('listData.owner ' + listData.owner);
text = String(listData.rowIndex + 1);
}
}
}

Related

Error #1009, navigator is null

I've got some problem in Flex. Basically I want to navigate to other pages by using navigator.pushview from list through custom item renderer. This is my CustomItemRender.as. Edit:
package renderer
{
import flash.events.MouseEvent;
import mx.core.FlexGlobals;
import mx.events.FlexEvent;
import mx.events.ItemClickEvent;
import spark.components.LabelItemRenderer;
import spark.components.NavigatorContent;
import spark.components.ViewNavigator;
public class CustomItemRender extends LabelItemRenderer
{
protected var var_A:Image;
[Bindable]
public var navigator:ViewNavigator = new ViewNavigator();
public function PrgListItemRenderer()
{
super();
}
override public function set data(value:Object):void
{
super.data = value;
}
override protected function createChildren():void
{
super.createChildren();
if(!takeAtt)
{
var_A= new Image();
var_A.source = "data/pics/var_A.png";
var_A.width = 23;
var_A.height = 23;
var_A.buttonMode = true;
var_A.addEventListener(MouseEvent.CLICK, var_AItem);
addChild(var_A);
}
}
override protected function measure():void
{
super.measure();
// measure all the subcomponents here and set measuredWidth, measuredHeight,
// measuredMinWidth, and measuredMinHeight
}
/**
* #private
*
* Override this method to change how the background is drawn for
* item renderer. For performance reasons, do not call
* super.drawBackground() if you do not need to.
*/
override protected function drawBackground(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.drawBackground(unscaledWidth, unscaledHeight);
// do any drawing for the background of the item renderer here
if(selected || showsCaret)
{
graphics.beginFill(0xffffff, 1);
graphics.endFill();
}
}
public function var_AItem(event:MouseEvent):void
{
trace("navigator: "+navigator);
navigator.pushView(nextView); //this is the line that have error #1009
}
}
}
But I got Error #1009. Help me please. Thanks.
I think it's a bad idea to listen to the click event inside the item renderer.
Your basic setup should look something like this:
->ViewNavigatorApplication>
-->SomeCustomView
---> SomeListBasedComponent id="list" itemRenderer="someCustomRenderer"
Fill the list with some data, which will be presented by your itemRenderer.
Now listen to the "IndexCangeEvent" of the list (from your view) and handle the 'click' there
To your view add:
private function init():void
{
list.addEventListener(IndexChangeEvent.CHANGE , onIndexChange );
}
protected function onIndexChange(e:IndexChangeEvent):void
{
// find out which item was selected. You can use the selectedItem property for this
var item:Object = list.selectedItem;
// start the view;
navigator.pushView(MyViewClass , item.someViewData );
}
Your view will hold the reference to the ViewNavigator.
P.S. dont forget to call the init() function onCreationComplete() of your view.
To your view declaration add:
View ... creationComplete="init()" >

Row renderer with height depending on row index

I have an IDropInListItemRenderer that is being used inside a List. The height of the row depends on both the data and it's position in the List's view.
How do I remeasure as soon as the row index changes?
updateDisplayList doesn't get called every time, and also by that point it's too late because the List has already measured it.
edit
The effect I'm trying to achieve is similar to iOS where the header for a section sticks to the top
Here is the basic renderer that doesn't measure correctly:
import mx.controls.Label;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;
public class MyRowRenderer extends UIComponent implements IListItemRenderer, IDropInListItemRenderer {
private static const HEADER_HEIGHT:int = 20;
private static const LABEL_HEIGHT:int = 20;
private var _data:Object;
private var _label:Label;
private var _header:Label;
private var _listData:BaseListData;
public function MyRowRenderer() {
super();
}
[Bindable("dataChange")]
public function get data():Object {
return _data;
}
public function set data(value:Object):void {
this._data = value;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
[Bindable("dataChange")]
public function get listData():BaseListData {
return _listData;
}
public function set listData(value:BaseListData):void {
_listData = value;
}
override protected function createChildren():void {
super.createChildren();
_header = new Label();
addChild(_header);
_label = new Label();
addChild(_label);
}
override protected function measure():void {
super.measure();
if (_label == null || _header == null) {
return;
}
var h:Number = LABEL_HEIGHT;
if (_listData.rowIndex == 0) {
h += HEADER_HEIGHT;
}
explicitHeight = measuredHeight = height = h;
}
override protected function commitProperties():void {
super.commitProperties();
_label.text = _data.label;
_header.text = _data.header;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
_header.visible = _header.includeInLayout = (_listData.rowIndex == 0);
if (_header.visible) {
_header.move(0, 0);
_header.setActualSize(unscaledWidth, HEADER_HEIGHT);
_label.move(0, HEADER_HEIGHT);
_label.setActualSize(unscaledWidth, LABEL_HEIGHT);
} else {
_label.move(0, 0);
_label.setActualSize(unscaledWidth, LABEL_HEIGHT);
}
}
}
}
I ended up creating a subclass of List and overriding the shiftRow function to dispatch a custom RowIndexChangeEvent. In the row renderer's set listData it adds an event listener to the owner for the RowIndexChangeEvent and invalidates it's properties, size and displaylist.

could not be able set labelField of List Control in flex 4.6

I'm not able to set the Label of List Control which I first save to SQLite database and then show that as lebelField of List control my code is following :
List of Cities mxml is :
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Cities"
>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import model.DataModel;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import mx.events.IndexChangedEvent;
import spark.components.SplitViewNavigator;
import spark.components.ViewNavigator;
import spark.transitions.ViewTransitionBase;
protected function myList_changeHandler():void {
// Create a reference to the SplitViewNavigator.
var splitNavigator:SplitViewNavigator = navigator.parentNavigator as SplitViewNavigator;
// Create a reference to the ViewNavigator for the Detail frame.
var detailNavigator:ViewNavigator = splitNavigator.getViewNavigatorAt(1) as ViewNavigator;
detailNavigator.transitionsEnabled = false;
// Change the view of the Detail frame based on the selected List item.
detailNavigator.pushView(DisplayContents, list_of_cities.selectedItem);
}
]]>
</fx:Script>
<s:VGroup width="100%" height="100%">
<s:List id="list_of_cities" height="100%" width="100%" change="myList_changeHandler();"
dataProvider="{DataModel.getInstance().cityList}">
</s:List>
</s:VGroup>
city value object is ::
package valueobject
{
[Bindable]
public class CityValueObject
{
public var id:uint;
public var nameofcity:String;
}}
and DataModel is ::
package model
{
import flash.data.SQLConnection;
import mx.collections.ArrayCollection;
[Bindable]
public class DataModel
{
public var connection:SQLConnection;
public var cityList:ArrayCollection = new ArrayCollection();
public var logs:String="Application Logs........\n";
public static var _instance:DataModel;
public static function getInstance():DataModel
{
if(_instance == null)
{
_instance = new DataModel();
}
return _instance;
}
}}
and CityUtilities class is:
package utillities
{
import flash.data.SQLResult;
import flash.data.SQLStatement;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import model.DataModel;
import mx.collections.Sort;
import mx.collections.SortField;
import valueobject.CityValueObject;
public class CityUtils
{
public static function getAllCities():void
{
var contactListStatement:SQLStatement = new SQLStatement();
contactListStatement.sqlConnection = DataModel.getInstance().connection;
contactListStatement.text = "SELECT * FROM CITYNAME";
contactListStatement.execute();
var result:SQLResult = contactListStatement.getResult();
if( result.data!=null)
{
DataModel.getInstance().cityList.removeAll();
for(var count:uint=0;count<result.data.length;count++)
{
var cityVO:CityValueObject = new CityValueObject();
cityVO.id = result.data[count].id;
cityVO.nameofcity = result.data[count].city;
DataModel.getInstance().cityList.addItem(cityVO);
}
}
sortData();
}
public static function sortData():void
{
var dataSortField:SortField = new SortField();
dataSortField.name = "nameofcity";
dataSortField.numeric = false;
/* Create the Sort object and add the SortField object created earlier to the array of fields to sort on. */
var numericDataSort:Sort = new Sort();
numericDataSort.fields = [dataSortField];
/* Set the ArrayCollection object's sort property to our custom sort, and refresh the ArrayCollection. */
DataModel.getInstance().cityList.sort = numericDataSort;
DataModel.getInstance().cityList.refresh();
}
public static function updateLog(newLog:String):void
{
DataModel.getInstance().logs += new Date().time+" :-> "+newLog+"\n";
}
}}
Please tell me how to set labelField according to SQLite nameofcity column thanks in advance
You need to display nameofcity means set LabelField property for spark list like following
<s:VGroup width="100%" height="100%">
<s:List id="list_of_cities" height="100%" width="100%" labelField="nameofcity" change="myList_changeHandler();"
dataProvider="{DataModel.getInstance().cityList}">
</s:List>
if it shows [object CityValueObject] then create custom ItemRenderer then override data method
override public function set data(value:Object):void
{
super.data = value;
var vo:CityValueObject=value as CityValueObject;
lblCityName.text = vo.nameofcity.toString();
}
All other code is correct, but mistake is that I placed
for(var count:uint=0;count<result.data.length;count++)
{
var cityVO:CityValueObject = new CityValueObject();
cityVO.id = result.data[count].id;
//cityVO.nameofcity = result.data[count].city; // Here I should Have written like
cityVO.nameofcity = result.data[count].nameofcity;// this works as I needed
DataModel.getInstance().cityList.addItem(cityVO);
}

Stop List From Scrolling

I have a datagrid that's using a custom itemrender with a button in it. I have a problem whereby when I click on a button the datagrid list scrolls its self. How can I prevent this behaviour?
Extended datagrid
<mx:DataGrid xmlns:mx="http://www.adobe.com/2006/mxml"
doubleClickEnabled="false"
alternatingItemColors="[#ffffff, #f1f1f1]"
mouseDown="mdhandler(event)"
sortableColumns="true"
xmlns:datagrids="components.content.contents.datagrids.*"
creationComplete="_creationcomplete(event)">
<mx:Script>
<![CDATA[
import mx.binding.utils.BindingUtils;
import components.preview.PreviewPlayEvent;
import mx.controls.Button;
import components.preview.PreviewPlay;
import components.content.contents.FixedHelp.FixedHelp;
import mx.events.FlexEvent;
import mx.core.Application;
import mx.events.ToolTipEvent;
import flash.utils.describeType;
import mx.utils.ObjectUtil;
import components.remix.PadDisplay.PadContent;
import components.remix.PadDisplay.PalletteCode;
import mx.core.IUIComponent;
import mx.controls.Image;
import mx.managers.DragManager;
import mx.core.DragSource;
import mx.events.DragEvent;
// Embed icon image.
[Embed(source='assets/proxy.png')]
public var dragImg:Class;
private var _scrollEnabled:Boolean=true;
private var _currentpath:String="";
public static const TYPE_ARRAY:Array=["loop", "one-shot sample", "stem", "track"]
private function _creationcomplete(e:FlexEvent=null):void
{
addEventListener(Event.ENTER_FRAME, enterframe, false, 0, true);
addEventListener(PreviewPlayEvent.PLAY, previewplayrequest, false, 0, true);
addEventListener(PreviewPlayEvent.STOP,previewstoprequest,false,0,true)
}
private function previewplayrequest(e:PreviewPlayEvent):void
{
_currentpath=e._path;
//Application.application.previewplay(e._path)
invalidateList();
}
private function previewstoprequest(e:PreviewPlayEvent):void
{
_currentpath="";
//Application.application.previewplay(e._path)
invalidateList();
}
public function get critera():String
{
return _currentpath;
}
public function set criteria(value:String):void
{
_currentpath=value;
}
private function enterframe(e:Event):void
{
if (Application.application.hashelp == false)
{
}
else
{
removeEventListener(Event.ENTER_FRAME, enterframe);
var xml:XML=Application.application.helpxml;
var fh:FixedHelp=Application.application.fixedhelp;
var helptext:String=Application.application.helpxml.studio.loops.text;
fh.addItem(helptext, this);
}
}
private function mdhandler(e:MouseEvent):void
{
describeType(e.currentTarget)
e.currentTarget.toString()
if (e.currentTarget.selectedItem != null && getQualifiedClassName(e.target) != "mx.controls::Button")
{
_scrollEnabled=false
var dragdata:XML=XML(e.currentTarget.selectedItem) as XML
var ds:DragSource=new DragSource()
var dragdataasObject:Object=ObjectUtil.copy(e.currentTarget.selectedItem)
ds.addData(dragdataasObject, PadContent.LOOP_FORMAT);
//trace(ds.dataForFormat(PadContent.LOOP_FORMAT)) // I expect xml data to trace out here but it does not
var imageproxy:Image=new Image();
imageproxy.source=dragImg;
imageproxy.height=40;
imageproxy.width=40;
DragManager.doDrag(this, ds, e, imageproxy, (this.x - e.stageX) + 40, (this.y - e.stageY) + 130)
this.selectedItem=null;
}
else
{
e.stopImmediatePropagation();
}
}
private function userLabelFunction(item:Object, column:DataGridColumn):String
{
var newstr:String=new String(item.t + "\n" + TYPE_ARRAY[item.ty])
return newstr;
}
private function catFunction(item:Object, column:DataGridColumn):String
{
var newstr:String=TYPE_ARRAY[item.ty];
return newstr;
}
private function bpmFunction(item:Object, column:DataGridColumn):int
{
var newstr:Number=new Number(item.bp);
return newstr;
}
private function beatCountFunction(item:Object, column:DataGridColumn):int
{
var newstr:int=new int(item.bc);
return newstr;
}
private function sortbpm(obj1:Object, obj2:Object):int
{
var valueA:Number=obj1.bp;
var valueB:Number=obj2.bp;
return ObjectUtil.numericCompare(valueA, valueB);
}
private function sortbeatcount(obj1:Object, obj2:Object):int
{
var valueA:Number=obj1.bc;
var valueB:Number=obj2.bc;
return ObjectUtil.numericCompare(valueA, valueB);
}
public function get scrollEnabled():Boolean
{
return _scrollEnabled;
}
public function set scrollEnabled(value:Boolean):void
{
_scrollEnabled=value;
}
override protected function dragScroll():void
{
if (_scrollEnabled)
{
super.dragScroll();
}
}
]]>
</mx:Script>
<mx:columns>
<mx:DataGridColumn dataField="r"
headerText=""
itemRenderer="components.content.contents.datagrids.ImageRendererLoops"/>
<mx:DataGridColumn dataField="t"
headerText="Title"
labelFunction="userLabelFunction"/>
<mx:DataGridColumn dataField="bc"
headerText="Beats"
labelFunction="beatCountFunction"
sortCompareFunction="sortbeatcount"/>
<mx:DataGridColumn dataField="BPM"
headerText="BPM"
labelFunction="bpmFunction"
sortCompareFunction="sortbpm"/>
<mx:DataGridColumn itemRenderer="components.preview.PreviewPlay"/>
</mx:columns>
</mx:DataGrid>
Item Renderer
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml"
implements="mx.controls.listClasses.IDropInListItemRenderer">
<mx:Script>
<![CDATA[
import mx.controls.DataGrid;
import components.content.contents.datagrids.LoopMastersDataGrid;
import components.remix.PadDisplay.PadContent;
import mx.controls.listClasses.BaseListData;
[Embed(source='assets/play_20.png')]
[Bindable]
public var play20Img:Class;
public static const PLAYING:String="playing";
public static const LOADING:String="loading";
public static const IDLE:String="idle";
private var _listData:BaseListData;
private var _bpm:int;
private var _path:String;
private var _state:String="idle";
public function get listData():BaseListData
{
return _listData;
}
public function set listData(value:BaseListData):void
{
_listData=value;
var criteria:String = (_listData.owner as LoopMastersDataGrid).critera;
if(this._path==criteria)
{
// this track is playing
previewbutton.styleName="stopPreviewButtonStyle";
}else
{
// this track is not playing
previewbutton.styleName="playPreviewButtonStyle";
}
//trace("list data crit=="+criteria);
}
override public function set data(value:Object):void
{
super.data=value;
this._path=new String(PadContent.LOOP_ROOT + value.r + "/" + value.u + ".rocudo");
this._bpm=value.bp;
//trace(this._path);
}
private function sendPreviewRequest(event:Event):void
{
switch(this._state)
{
case(PreviewPlay.IDLE):
(listData.owner as LoopMastersDataGrid).dispatchEvent(new PreviewPlayEvent(PreviewPlayEvent.PLAY,this._bpm,this._path));
break;
case(PreviewPlay.LOADING):
// preview play probably shouldnt care about loading
break;
case(PreviewPlay.PLAYING):
(listData.owner as LoopMastersDataGrid).dispatchEvent(new PreviewPlayEvent(PreviewPlayEvent.STOP,this._bpm,this._path));
break;
}
//dispatchEvent(new PreviewPlayEvent(PreviewPlayEvent.PLAY,this._bpm,this._path));
}
/** override protected function commitProperties():void
{
super.commitProperties();
this._state=PreviewPlay.IDLE;
}**/
]]>
</mx:Script>
<mx:Button id="previewbutton"
width="20"
height="20" click="sendPreviewRequest(event)" styleName="playPreviewButtonStyle">
</mx:Button>
</mx:HBox>
Update
I removed my mouse down handler from the datagrid and removed the cusom component. Clicking on a item in the datagrid makes the datagrid scroll so that the item selected appears first in the list. This is the behaviour I need to stop.

Bindable variable not updating with viewstack / swiz

I'm using the Swiz framework and I'm trying to update my viewstack's selectedIndex with a bindable property. It gets to my event handler which updates the bindable variable but the Main app file's viewstack never realizes it. What could be the issue?
thx
-Mike
================================
MAIN APP FILE
<mx:Script>
<![CDATA[
import reg.model.ApplicationViewModel;
import beyaz.reg.swiz.SwizBeans;
import org.swizframework.Swiz;
[Autowire(bean="applicationViewModel")]
[Bindable]
public var applicationViewModel:ApplicationViewModel;
private function preInitialize():void {
Swiz.loadBeans( [ SwizBeans ] );
}
]]>
</mx:Script>
<mx:ViewStack id="theViewstack" **selectedIndex=" {applicationViewModel.mainViewIndex}"** width="100%" height="100%">
<prescreen:Prescreen id="prescreenView"/>
<login:Login id="loginView"/>
<profile:Profile id="profileView"/>
</mx:ViewStack>
=================================
ApplicationViewModel
package com.reg.model
{
public class ApplicationViewModel
{
public static const PRESCREEN_VIEW:int = 0;
public static const LOGIN_VIEW:int = 1;
public static const PRSNL_INFO_VIEW:int = 2;
[Bindable]
public var message:String = "";
[Bindable]
public var mainViewIndex:int = PRESCREEN_VIEW;
}
}
===========================
Controller
package com.reg.controller
{
import com.reg.model.ApplicationViewModel;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.DynamicEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.core.Application;
import org.swizframework.Swiz;
import org.swizframework.controller.AbstractController;
public class PrescreenController// extends AbstractController
{
public static const START_REGISTRATION:String = "startReg";
[Autowire(bean="applicationViewModel")]
[Bindable]
public var applicationViewModel:ApplicationViewModel;
[Mediate(event="startReg")]
public function startReg():void
{
//CODE GETS TO HERE!
applicationViewModel.mainViewIndex = ApplicationViewModel.PRSNL_INFO_VIEW;
}
}
}
I got bit by this problem just last week.
Put your [Bindable] tag before the other tags. For some reason the Flex compiler doesn't fold in the appropriate PropertyChangeEvent dispatching unless you put the [Bindable] tag first.

Resources