I'm creating a custom legend using legendItemClass to highlight each item when the user rolls over, remove it when they roll off and highlight it a little differently when they click. That all works just fine but I also want the pie wedge to explode out when the user click. I have the piece of code but it requires a LegendMouseEvent. I'm also thinking I may be able to re-write the code if I could get the displayName but I'm a loss with that as well. Here's what I have:
Legend:
<mx:Legend id="legend" width="100%" direction="vertical"
labelPlacement="right" markerHeight="10" markerWidth="10"
legendItemClass="CustomPieLegendItem"
itemClick="explode(event)"
color="{_color}" />
Explode:
public function explode(event:LegendMouseEvent) : void {
var len : Number = PieSeries(event.item.source).legendData.length;
var index : Number = 0;
var arr:Array = new Array(len);
if(over){
event.item.alpha = 0.70;
} else {
event.item.alpha = 0.70;
for(var i : Number = 0; i < len ; i++){
if(event.item.label == event.item.source.legendData[i].label){
index = i;
arr[i] = 0.1;
}else {
arr[i] = 0;
}
}
PieSeries(event.item.element).perWedgeExplodeRadius = arr;
}
}
Class:
package
{
import custom.charts;
import flash.display.Sprite;
import flash.events.MouseEvent;
import ilog.core.ilog_internal;
import mx.charts.LegendItem;
import mx.charts.events.LegendMouseEvent;
import mx.charts.series.PieSeries;
import mx.charts.series.items.PieSeriesItem;
public class CustomPieLegendItem extends LegendItem {
public function CustomPieLegendItem(){
super();
}
private var mouseSprite:Sprite;
private var state:int=0;
private static var IDLE_STATE:int=0;
private static var OVER_STATE:int=1;
private static var SELECTED_STATE:int=2;
public function get selected():Boolean{
return (state == OVER_STATE || state == SELECTED_STATE);
}
public function set selected(value:Boolean):void {
if (value)
state=SELECTED_STATE;
else
state=IDLE_STATE;
invalidateDisplayList();
}
private function clickHandler(e:MouseEvent):void {
charts(document).resetSeries();
state=SELECTED_STATE;
// returns the item USED for the displayName, not the actual name
trace((element as PieSeries).displayName);
// does not work because I need to pass a LegendMouseEvent
charts(document).explode("NEEDS LegendMouseEvent HERE");
invalidateDisplayList();
}
private function overHandler(e:MouseEvent):void {
if (state != SELECTED_STATE){
state=OVER_STATE;
invalidateDisplayList();
}
}
private function outHandler(e:MouseEvent):void {
if (state != SELECTED_STATE){
state=IDLE_STATE;
invalidateDisplayList();
}
}
override protected function createChildren():void {
super.createChildren();
mouseSprite=new Sprite();
addChild(mouseSprite);
mouseSprite.addEventListener(MouseEvent.MOUSE_OVER, overHandler);
mouseSprite.addEventListener(MouseEvent.MOUSE_OUT, outHandler);
mouseSprite.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler);
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth, unscaledHeight);
mouseSprite.graphics.clear();
mouseSprite.graphics.beginFill(0, 0);
mouseSprite.graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.clear();
if (state == OVER_STATE || state == SELECTED_STATE)
{
graphics.beginFill(0xCCCCCC, 0.2);
if (state == SELECTED_STATE)
graphics.lineStyle(1, 0xCCCCCC);
graphics.drawRect(-2, 0, width+2, height);
}
}
}
}
Why don't you just create a new LegendMouseEvent using below code new LegendMouseEvent(LegendMouseEvent.ITEM_CLICK, e, this) and pass it to explode.
you can use nay types from LegendMouseEvent class , e - MouseEvent passed to clickhandler
Related
I have a checkbox in AdvancedDataGrid as GroupItemrenderer.I have selected certain child nodes in tree and closed its parent,later when I reopen the same parent node,selected child nodes are not those that i selected.How to retain the correct selections in checkbox.??
Also am unable to set the value for checkbox (GroupItemRenderer) by default from component and also unable to access the value for checkbox in Data property.
package
{
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.ui.Keyboard;
import mx.collections.ICollectionView;
import mx.collections.IHierarchicalCollectionView;
import mx.collections.IHierarchicalData;
import mx.collections.IViewCursor;
import mx.controls.AdvancedDataGrid;
import mx.controls.Alert;
import mx.controls.CheckBox;
import mx.controls.Image;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.advancedDataGridClasses.AdvancedDataGridGroupItemRenderer;
import mx.controls.advancedDataGridClasses.AdvancedDataGridListData;
import mx.core.FlexGlobals;
import mx.core.mx_internal;
use namespace mx_internal;
public class CheckADGRenderer extends AdvancedDataGridGroupItemRenderer
{
protected var myImage:Image;
public var status:String = "false";
// set image properties
private var imageWidth:Number = 6;
private var imageHeight:Number = 6;
private var inner:String = "inner.png";
protected var myCheckBox:CheckBox;
static private var STATE_SCHRODINGER:String = "schrodinger";
static private var STATE_CHECKED:String = "checked";
static private var STATE_UNCHECKED:String = "unchecked";
public function CheckADGRenderer ()
{
super();
mouseEnabled = false;
}
private function toggleParents(item:Object,adg:AdvancedDataGrid,state:String):void
{
if (item == null)
{
return;
}
else
{
item.#state = false;
toggleParents(adg.getParentItem(item), adg, getState (adg, adg.getParentItem(item)));
}
}
private function toggleChildren (item:Object, adg:AdvancedDataGrid, state:String):void
{
if (item == null)
{
return;
}
else
{
//item.#state = state;
var adgCollection:IHierarchicalCollectionView = adg.dataProvider as IHierarchicalCollectionView;
var adgHD:IHierarchicalData = adgCollection.source;
if (adgHD.hasChildren(item))
{
var children:ICollectionView = adgCollection.getChildren (item);
var cursor:IViewCursor = children.createCursor();
while (!cursor.afterLast)
{
toggleChildren(cursor.current, adg, state);
cursor.moveNext();
}
}
}
}
private function getState(adg:AdvancedDataGrid, parent:Object):String
{
var noChecks:int = 0;
var noCats:int = 0;
var noUnChecks:int = 0;
if (parent != null)
{
var adgCollection:IHierarchicalCollectionView = adg.dataProvider as IHierarchicalCollectionView;
var cursor:IViewCursor = adgCollection.getChildren(parent).createCursor();
}
if ((noChecks > 0 && noUnChecks > 0) || (noCats > 0))
{
return STATE_SCHRODINGER;
}
else if (noChecks > 0)
{
return STATE_CHECKED;
}
else
{
return STATE_UNCHECKED;
}
}
private function imageToggleHandler(event:MouseEvent):void
{
myCheckBox.selected = !myCheckBox.selected;
checkBoxToggleHandler(event);
}
var selectArr:Array = new Array();
private function checkBoxToggleHandler(event:MouseEvent):void
{
if (data)
{
var myListData:AdvancedDataGridListData = AdvancedDataGridListData(this.listData);
var selectedNode:Object = myListData.item;
var adg:AdvancedDataGrid = AdvancedDataGrid(myListData.owner);
var toggle:Boolean = myCheckBox.selected;
if (toggle)
{
toggleChildren(data, adg, STATE_CHECKED);
}
else
{
toggleChildren(data, adg, STATE_UNCHECKED);
}
var parent:Object = adg.getParentItem (data);
toggleParents (parent, adg, getState (adg, parent));
//Alert.show(selectArr.toString()+"\t\t"+selectArr.length+"\t\t"+selectArr[0].length+"\t\t"+adg.selectedIndices.length);
}
}
override protected function createChildren():void
{
super.createChildren();
myCheckBox = new CheckBox();
myCheckBox.setStyle( "verticalAlign", "middle" );
myCheckBox.addEventListener( MouseEvent.CLICK, checkBoxToggleHandler );
addChild(myCheckBox);
}
private function setCheckState (checkBox:CheckBox, value:Object, state:Boolean):void
{
if (state == STATE_CHECKED)
{
checkBox.selected = state;
}
else if (state == STATE_UNCHECKED)
{
checkBox.selected = false;
}
else if (state == STATE_SCHRODINGER)
{
checkBox.selected = false;
}
checkBox.selected = state;
}
override public function set data(value:Object):void
{
super.data = value;
var myListData:AdvancedDataGridListData = AdvancedDataGridListData(this.listData);
//var adg:AdvancedDataGrid = AdvancedDataGrid(myListData.owner);
var selectedNode:Object = myListData.item;
myCheckBox.selected = AdvancedDataGridListData(super.listData).item.show;
//var adg:AdvancedDataGrid = AdvancedDataGrid(myListData.owner);
//setCheckState (myCheckBox, value, Boolean(value.state));
}
override protected function commitProperties():void
{
super.commitProperties();
var dg:AdvancedDataGrid = AdvancedDataGrid(listData.owner);
var column:AdvancedDataGridColumn =
dg.columns[listData.columnIndex];
label.wordWrap = dg.columnWordWrap(column);
}
/**
* #private
*/
override protected function measure():void
{
super.measure();
var w:Number = data ? AdvancedDataGridListData(listData).indent : 0;
if (disclosureIcon)
w += disclosureIcon.width;
if (icon)
w += icon.measuredWidth;
if (myCheckBox)
w += myCheckBox.measuredWidth;
// guarantee that label width isn't zero because it messes up ability to measure
if (label.width < 4 || label.height < 4)
{
label.width = 4;
label.height = 16;
}
if (isNaN(explicitWidth))
{
w += label.getExplicitOrMeasuredWidth();
measuredWidth = w;
}
else
{
label.width = Math.max(explicitWidth - w, 4);
}
measuredHeight = label.getExplicitOrMeasuredHeight();
if (icon && icon.measuredHeight > measuredHeight)
measuredHeight = icon.measuredHeight;
if (myCheckBox && myCheckBox.measuredHeight > measuredHeight)
measuredHeight = myCheckBox.measuredHeight;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(super.data)
{
if (super.icon != null)
{
myCheckBox.x = super.icon.x;
myCheckBox.y = (unscaledHeight - myCheckBox.height) / 2;
super.icon.x = myCheckBox.x + myCheckBox.width + 17;
if (icon.x + icon.width > unscaledWidth)
icon.setActualSize(0, unscaledHeight);
super.label.x = super.icon.x + super.icon.width + 3;
super.label.setActualSize(Math.max(unscaledWidth - super.label.x, 4), unscaledHeight);
}
else
{
myCheckBox.x = super.label.x;
myCheckBox.y = (unscaledHeight - myCheckBox.height) / 2;
super.label.x = myCheckBox.x + myCheckBox.width + 17;
super.label.setActualSize(Math.max(unscaledWidth - super.label.x, 4), unscaledHeight);
}
if (myCheckBox.x + myCheckBox.width > unscaledWidth)
myCheckBox.visible = false;
}
trace(label.width);
}
}
}
I am using 2D array as dataprovider to ADG.
My post :
how to add an attribute to an existing array dynamically in Flex , is associated with the above post.
I suspect you haven't written any code to set the default state of the Checkbox based on data.
Generically, your itemRenderer's Checkbox state should be stored as part of the data. When the
itemRenderer is initialized, set the checkbox to checked--or not--based on the property in your data element. When the Checkbox is clicked; change that same property in your data element.
This approach will sync your itemRenderer's display state up with your data.
If you want a more specific answer, you'll have to share some code so we can see what you're doing.
I have plot chart with an 'itemRenderer' to render the text. I have the renderer working corrrectly. Now, what I want to show/hide some of the chart tiems if it's below/above some value. I am using the 'Label' component as 'itemRenderer' and overriding the 'data' function (Please find my renderer component below..).
If the size of the dataprovider of the chart is greater than '100', I want to hide all the 'events' with 'low' severity. For that I need to get a reference to the dataprovider, but not sure how... ?
Has anyone come across this problem ?
public class EventChartItemRenderer extends Label
{
private var chartItem:ChartItem;
private var evtLblTxt:String;
private var txtBorderColor:uint;
private var showHide:Boolean;
public function EventChartItemRenderer()
{
super();
}
override public function set data(val:Object):void {
chartItem = val as ChartItem;
var event:DeviceEventDetails = chartItem.item as DeviceEventDetails;
if(event.severity == 2){
evtLblTxt = "2";
txtBorderColor = 0xFFD685;
showHide = false;
}else if(event.severity == 4){
evtLblTxt = "4";
txtBorderColor = 0xE68D53;
showHide = true;
}else if(event.severity == 6){
evtLblTxt = "6";
txtBorderColor = 0xB870DB;
showHide = true;
}
}
override public function get data():Object {
return chartItem;
}
override protected function commitProperties():void {
super.commitProperties();
if(!showHide) return;
this.text = evtLblTxt;
this.setStyle("color", txtBorderColor);
this.setStyle("fontWeight", "bold");
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(!showHide) return;
var rc:Rectangle = new Rectangle(0, 0, unscaledWidth, unscaledHeight);
var g:Graphics = graphics;
g.clear();
g.moveTo(rc.left, rc.top);
g.lineStyle(1, txtBorderColor, 0.8);
g.lineTo(rc.width, rc.top);
g.lineTo(rc.width, rc.bottom+4);
g.lineTo(rc.left, rc.bottom+4);
g.lineTo(rc.left, rc.top);
g.endFill();
}
}
It's actually chartItem.element. And you need to cast it to 'ChartElement' because the 'element' refers to an interface 'IChartElement' and access the 'dataProvider' property. The 'chartDataProvider' is write-only. So, the correct way of accessing it is...
(chartItem.element as ChartElement).dataProvider.
ChartItem has an IChartElement property with access to the chart data provider:
chartItem.item.chartDataProvider.length > 100
I have a Custom Spark ComboBox which contains a list of items. I want to Add a new Item to the ComboBox stating the text "Add new Item", which on selecting popup a window to do some operations.
I have achieved this by creating a new object of the same type of the entities in the data provider, with the LabelField of the new object having the text "Add new Item". I have override the set dataProvider method and in the custom combobox.
But this adds the values to the Actual List which is binded to the DataProvider. The list is used in the business logics. So i do not want this to happen. I have lot of Entity Classes. i could not change all the objects.
All i want is to achieve the same functionality in my custom component without changing the other code. I have also tried to create a new instance of dataProvier but i noticed that the binding of the List and dataprovider is lost when i created a new instance.
Kindly help.!!
Edited:
ExtendedComboBox.as
package components
{
import flash.utils.getDefinitionByName;
import mx.collections.ArrayCollection;
import mx.collections.IList;
import spark.components.ComboBox;
import spark.events.DropDownEvent;
public class ExtendedComboBox extends ComboBox
{
private var _addItem:Boolean = false;
private var _addItemLabel:String = "Create New Item" ;
private var _dropDownClass:String = null ;
private var originalDP:IList ;
private var dpEdited:Boolean = false;
public function ExtendedComboBox()
{
super();
this.addItem = true;
this.addEventListener(DropDownEvent.CLOSE, dropDownCloseEventListner );
this.addEventListener(DropDownEvent.OPEN, openDropDownEvent );
}
public function get dropDownClass():String
{
return _dropDownClass;
}
public function set dropDownClass(value:String):void
{
_dropDownClass = value;
}
public function get addItemLabel():String
{
return _addItemLabel;
}
public function set addItemLabel(value:String):void
{
_addItemLabel = value;
}
public function get addItem():Boolean
{
return _addItem;
}
public function set addItem(value:Boolean):void
{
_addItem = value;
}
private function dropDownCloseEventListner(event:DropDownEvent):void{
}
protected function openDropDownEvent(event:DropDownEvent):void{
if(addItem)
{
// if(value) value = new ArrayCollection();
var value:IList ;
if(originalDP == null) value = new ArrayCollection ;
else value = new ArrayCollection( originalDP.toArray() ) ;
var tempObj:Object;
var createItemPresent:Boolean =false ;
if(dropDownClass != null)
{
var TempClass = flash.utils.getDefinitionByName(dropDownClass) as Class;
tempObj = new TempClass();
if(value.length >0)
{
// trace(value.getChildAt(0)[this.labelField]) ;
if(value.getItemAt(0)[this.labelField] == addItemLabel)
createItemPresent = true ;
}
if(!createItemPresent)
{
tempObj[this.labelField] = addItemLabel ;
var sort = (value as ArrayCollection).sort ;
value.addItemAt(tempObj, 0);
(value as ArrayCollection).sort = sort ;
dpEdited = true;
}
}
}
super.dataProvider = value;
}
override public function set dataProvider(value:IList):void{
if(!dpEdited)
{
originalDP = value;
dpEdited = true;
}
/*if(addItem)
{
// if(value) value = new ArrayCollection();
var tempObj:Object;
var createItemPresent:Boolean =false ;
if(dropDownClass != null)
{
var TempClass = flash.utils.getDefinitionByName(dropDownClass) as Class;
tempObj = new TempClass();
if(value.length >0)
{
if(value.getItemIndex(0)[this.labelField] == addItemLabel)
createItemPresent = true ;
}
if(!createItemPresent)
{
tempObj[this.labelField] = addItemLabel ;
var sort = (value as ArrayCollection).sort ;
value.addItemAt(tempObj, 0);
(value as ArrayCollection).sort = sort ;
}
}
}*/
super.dataProvider = value;
}
}
}
MyEntityObj.as
package entity
{
public class MyEntityObj
{
private var _name:String ;
private var _age:int ;
private var _company:String;
public function MyEntityObj()
{
}
public function get company():String
{
return _company;
}
public function set company(value:String):void
{
_company = value;
}
public function get age():int
{
return _age;
}
public function set age(value:int):void
{
_age = value;
}
public function get name():String
{
return _name;
}
public function set name(value:String):void
{
_name = value;
}
}
}
And Implementation Sample - ComboImpl.mxml
<?xml version="1.0" encoding="utf-8"?>
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
import mx.events.FlexEvent;
[Bindable]
private var samplDP:ArrayCollection;
protected function application1_initializeHandler(event:FlexEvent):void
{
samplDP = new ArrayCollection ;
samplDP.addEventListener(CollectionEvent.COLLECTION_CHANGE, changeHandlerFunc );
var sampVO:MyEntityObj;
for(var i:int = 0; i<5;i++)
{
sampVO = new MyEntityObj;
sampVO.name = "Name " + i;
sampVO.age = i;
sampVO.company = "Company " + i;
samplDP.addItem(sampVO);
}
}
protected function changeHandlerFunc(event:CollectionEvent):void{
var nameList:String = "" ;
for each(var myObj:* in samplDP)
{
nameList += myObj.name + ", " ;
}
changeHandler.text = nameList ;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:VGroup>
<s:Label id="changeHandler" />
<components:ExtendedComboBox dataProvider="{samplDP}" labelField="name" addItem="true" dropDownClass="entity.MyEntityObj" addItemLabel="Create Sample" />
</s:VGroup>
As commented in the set DataProvider Overridden method and the in the DropDownClose events addition of the new item directly affects the original List. i do not want this to happen.
Note that its just a sample implementation. The component creation in my project actually happens in the action script class dynamically.
Kindly help.!!
It sounds to me like:
You're going to have to extend the ComboBox (If you drill down into how it's implemented, possibly the DataGroup) to include your extra "Add New Item" item w/o it being in the dataProvider.
I expect this to be very difficult, but have not reviewed the code for this purpose.
I have a standard combobox that dispatches a collection event when the dataprovider finishes initializing:
my_cb.addEventListener( CollectionEvent.COLLECTION_CHANGE, getMyStuff );
Then I have a custom component that also has a dataProvider. How do I get it to dispatch a collection change event when its dataprovider finishes loading?
From what I've read, I can't do it. Will dispatching a propertychangeevent work?
Thanks for any helpful tips!
UPDATE:
I have a custom component that I call 'SortingComboBox' but it is not a ComboBox at all; it extends Button and I set is dataProvider property to my arraycollection, model.product (which is an arraycollection).
And here is how I use the dataProvider in that component:
code
[Bindable]
private var _dataProvider : Object;
public function get dataProvider() : Object
{
return _dataProvider;
}
public function set dataProvider(value : Object) : void
{
_dataProvider = value;
}
code
In the createChildren() method of this component, I use this:
BindingUtils.bindProperty(dropDown, "dataProvider", this, "dataProvider");
The dropDown is a custom VBox that I use to display labels.
When you call the setter, you have to make sure
1) that you actually are changing the value with the setter. So even if you are inside the class, call this.dataProvider = foo instead of _dataProvider = foo
2) The binding will not trigger unless you actually change the value. If you trace you'll see that the setter actually calls the getter, if the values of what you pass into the setter and the getter are the same, the binding will not occur.
Your other option is to put an event on the getter, then just call it to trigger the binding.
[Bindable( "somethingChanged" )]
public function get dataProvider() : Object
{
return _dataProvider;
}
dispatchEvent( new Event( "somethingChanged" ) );
Make your dataprovider bindable
[Bindable]
protected var _dataProvider:ArrayCollection ;
Data binding is something unique to ActionScript/Flex.
Among other things it will dispatch change events.
Maybe if you post your code for the custom component I can be more specific.
Actually can you explain what your goal is you are trying to achieve?
All I can tell is you are trying to make a button have a drop down.
Why?
this is the custom component just to give you a better idea.
code
package com.fidelity.primeservices.act.components.sortingcombobox
{
import com.fidelity.primeservices.act.events.component.ResetSortEvent;
import com.fidelity.primeservices.act.events.component.SortEvent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import mx.binding.utils.BindingUtils;
import mx.controls.Button;
import mx.core.UIComponent;
import mx.effects.Tween;
import mx.events.FlexMouseEvent;
import mx.managers.PopUpManager;
import mx.events.PropertyChangeEvent;
import mx.events.PropertyChangeEventKind;
public class SortingComboBox extends Button
{
private const MAX_LABEL_LENGTH : int = 400;
private const ELIPSES : String = "...";
[Bindable]
private var _dataProvider : Object;
private var dropDown : SortingDropDown;
private var inTween : Boolean;
private var showingDropdown : Boolean;
private var openCloseTween : Tween;
public var noSelectionLabel : String = "No Filter";
public var noSelectionData : String = "ALL";
public function get dataProvider() : Object
{
return _dataProvider;
}
public function set dataProvider(value : Object) : void
{
_dataProvider = value;
}
private function collectionEvent(e : Event):void
{
trace(new Date(), e);
}
public function SortingComboBox()
{
super();
this.buttonMode = true;
this.useHandCursor = true;
inTween = false;
showingDropdown = false;
addEventListener(Event.REMOVED_FROM_STAGE, removedFromStage);
}
override protected function createChildren() : void
{
super.createChildren();
dropDown = new SortingDropDown();
dropDown.width = 240;
dropDown.maxHeight = 300;
dropDown.visible = false;
BindingUtils.bindProperty(dropDown, "dataProvider", this, "dataProvider");
dropDown.styleName = "sortingDropDown";
dropDown.addEventListener(SortEvent.CLOSE_SORT, closeDropDown);
dropDown.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE, dropdownCheckForClose);
dropDown.addEventListener(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE, dropdownCheckForClose);
dropDown.addEventListener(SortEvent.UPDATE_SORT, onSortUpdate); //this event bubbles
dropDown.addEventListener(ResetSortEvent.RESET_SORT_EVENT, onSortUpdate);
PopUpManager.addPopUp(dropDown, this);
this.addEventListener(MouseEvent.CLICK, toggleDropDown);
// weak reference to stage
systemManager.addEventListener(Event.RESIZE, stageResizeHandler, false, 0, true);
}
private function stageResizeHandler(evt : Event) : void
{
showingDropdown = false;
dropDown.visible = showingDropdown;
}
private function toggleDropDown(evt : MouseEvent) : void
{
if(!dropDown.visible)
{
openDropDown(evt);
}
else
{
closeDropDown(evt);
}
}
private function openDropDown(evt : MouseEvent) : void
{
if (dropDown.parent == null) // was popped up then closed
{
PopUpManager.addPopUp(dropDown, this);
}
else
{
PopUpManager.bringToFront(dropDown);
}
showingDropdown = true;
dropDown.visible = showingDropdown;
dropDown.enabled = false;
var point:Point = new Point(0, unscaledHeight);
point = localToGlobal(point);
point = dropDown.parent.globalToLocal(point);
//if the dropdown is larger than the button and its
//width would push it offscreen, align it to the left.
if (dropDown.width > unscaledWidth && point.x + dropDown.width > screen.width)
{
point.x -= dropDown.width - unscaledWidth;
}
dropDown.move(point.x, point.y);
//run opening tween
inTween = true;
// Block all layout, responses from web service, and other background
// processing until the tween finishes executing.
UIComponent.suspendBackgroundProcessing();
dropDown.scrollRect = new Rectangle(0, dropDown.height, dropDown.width, dropDown.height);
openCloseTween = new Tween(this, dropDown.height, 0, 250);
}
private function closeDropDown(evt : Event) : void
{
//dropDown.visible = false;
showingDropdown = false;
//run closing tween
inTween = true;
// Block all layout, responses from web service, and other background
// processing until the tween finishes executing.
UIComponent.suspendBackgroundProcessing();
openCloseTween = new Tween(this, 0, dropDown.height, 250);
}
private function dropdownCheckForClose(event : MouseEvent) : void
{
if (event.target != dropDown)
// the dropdown's items can dispatch a mouseDownOutside
// event which then bubbles up to us
return;
if (!hitTestPoint(event.stageX, event.stageY, true))
{
closeDropDown(event);
}
}
public function refresh():void
{
onSortUpdate(null);
}
private function onSortUpdate(evt1 : Event) : void
{
//update the label
var dpLength : int = this.dataProvider.length;
var nextLabel : String = "";
var nextData : String = "";
for (var i : int = 0; i < dpLength; i++)
{
if (this.dataProvider[i].selected == true)
{
nextLabel += this.dataProvider[i].label + ", ";
if (this.dataProvider[i].data != null)
{
nextData += this.dataProvider[i].data + ", ";
}
}
}
if (nextLabel.length > 0)
{
// remove extra comma at end
nextLabel = nextLabel.substr(0, nextLabel.length - 2);
}
if (nextData.length > 0)
{
nextData = nextData.substr(0, nextData.length - 2);
}
if (nextLabel.length > MAX_LABEL_LENGTH)
{
// limit label to MAX_LABEL_LENGTH + ... REASON: tooltips with lots of characters take a long time to render
nextLabel = nextLabel.substr(0, MAX_LABEL_LENGTH) + ELIPSES;
}
if (nextLabel.length == 0)
{
nextLabel = noSelectionLabel;
//nextLabel = "No Filter";
}
if (nextData.length == 0)
{
nextData = noSelectionData;
//nextData = "ALL";
}
label = nextLabel;
data = nextData;
toolTip = label;
if (evt1 is SortEvent)
{
trace("sort event");
var temp:Object = this.dataProvider;
this.dataProvider = null;
this.dataProvider = temp;
this.refresh();
}
else
{
trace("not dispatching");
}
}
public function onTweenUpdate(value:Number):void
{
dropDown.scrollRect = new Rectangle(0, value, dropDown.width, dropDown.height);
}
public function onTweenEnd(value:Number) : void
{
// Clear the scrollRect here. This way if drop shadows are
// assigned to the dropdown they show up correctly
dropDown.scrollRect = null;
inTween = false;
dropDown.enabled = true;
dropDown.visible = showingDropdown;
UIComponent.resumeBackgroundProcessing();
}
private function removedFromStage(event:Event):void
{
if(inTween)
{
openCloseTween.endTween();
}
// Ensure we've unregistered ourselves from PopupManager, else
// we'll be leaked.
PopUpManager.removePopUp(dropDown);
}
}
}
Ok this code here
[Bindable]
private var _dataProvider : Object;
public function get dataProvider() : Object
{
return _dataProvider;
}
public function set dataProvider(value : Object) : void
{
_dataProvider = value;
}
is no different then
[Bindable]
public var _dataProvider : Object;
Since objects are passed by reference you are not protecting it in anyway and the setter and getter are pointless.
On the other hand you made the source _dataProvider Bindable so anytime the data changes it will dispatch a CollectionEvent.COLLECTION_CHANGE
How can I replace the loading clock in Flex at the cursor to something like loading wheel in the middle of page instead of cursor
I loathe that little clock. A clock on the mouse just tells the user that something is busy, but they don't know what. It is much better to display a progress indicator visually NEAR the thing that it is showing the progress of!
So, my solution is to enforce a ban on CursorManager, and instead supply your own progress indicator.
Example: A submit button in a form. You know that the submittal is asynchronous and it will take an indeterminate amount of time. So after the user clicks the button and the request is executed, display a little spinner to the direct right of the button. When the request is complete, hide the spinner. It's very sad to see a user who is worried that her actions did not accomplish anything--so give them a way of determining that your application is indeed functioning!
To go along with Jonathon Dumaine's answer, here's an example of the Spinner class I use as a busy indicator in my apps. Just remember to call the stop() method when you first load it since it will use memory in your app if it's playing even when visible is set to false. You can call the play() method when you want it to start spinning again.
Spinner.as
package {
import flash.events.TimerEvent;
import flash.utils.Timer;
import mx.core.FlexGlobals;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.styles.CSSStyleDeclaration;
import mx.styles.StyleManager;
[Style(name="tickColor",type="uint",format="Color",inherit="no")]
public class Spinner extends UIComponent {
private static var STYLE_TICK_COLOR:String = "tickColor";
private var tickColorChanged:Boolean;
private static var classConstructed:Boolean = classConstruct();
// Make sure we create the ticks the first time updateDisplayList is called
private var creation:Boolean = true;
private var fadeTimer:Timer;
private var _isPlaying:Boolean;
private var _numTicks:int = 12;
private var numTicksChanged:Boolean;
private var _size:Number = 30;
private var sizeChanged:Boolean;
private var _tickWidth:Number = 3;
private var tickWidthChanged:Boolean;
private var _speed:int = 1000;
[Bindable] public var fadeSpeed:int = 600;
public var autoPlay:Boolean = true;
public function Spinner() {
super();
addEventListener(FlexEvent.CREATION_COMPLETE, handleCreationComplete);
addEventListener(FlexEvent.REMOVE, handleUnloading)
}
private function handleCreationComplete(e:FlexEvent):void {
removeEventListener(FlexEvent.CREATION_COMPLETE, handleCreationComplete);
if (autoPlay) {
play();
}
}
/**
* Set the height and width based on the size of the spinner. This should be more robust, but oh well.
*/
override protected function measure():void {
super.measure();
width = _size;
height = _size;
}
/**
* Override the updateDisplayList method
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
if (tickColorChanged || numTicksChanged || sizeChanged || tickWidthChanged || creation) {
creation = false;
// Find out whether it's playing so we can restart it later if we need to
var wasPlaying:Boolean = _isPlaying;
// stop the spinning
stop();
// Remove all children
for (var i:int = numChildren - 1; i >= 0; i--) {
removeChildAt(i);
}
// Re-create the children
var radius:Number = size / 2;
var angle:Number = 2 * Math.PI / _numTicks; // The angle between each tick
var tickWidth:Number = (_tickWidth != -1) ? _tickWidth : size / 10;
var tickColor:uint = getStyle(STYLE_TICK_COLOR);
var currentAngle:Number = 0;
for (var j:int = 0; j < _numTicks; j++) {
var xStart:Number = radius + Math.sin(currentAngle) * ((_numTicks + 2) * tickWidth / 2 / Math.PI);
var yStart:Number = radius - Math.cos(currentAngle) * ((_numTicks + 2) * tickWidth / 2 / Math.PI);
var xEnd:Number = radius + Math.sin(currentAngle) * (radius - tickWidth);
var yEnd:Number = radius - Math.cos(currentAngle) * (radius - tickWidth);
var t:Tick = new Tick(xStart, yStart, xEnd, yEnd, tickWidth, tickColor);
t.alpha = 0.1;
this.addChild(t);
currentAngle += angle;
}
// Start the spinning again if it was playing when this function was called.
if (wasPlaying) {
play();
}
tickColorChanged = false;
numTicksChanged = false;
sizeChanged = false;
tickWidthChanged = false;
}
}
private static function classConstruct():Boolean {
if (!FlexGlobals.topLevelApplication.styleManager.getStyleDeclaration("Spinner")) {
// If there is no CSS definition for StyledRectangle,
// then create one and set the default value.
var newStyleDeclaration:CSSStyleDeclaration = new CSSStyleDeclaration();
newStyleDeclaration.setStyle(STYLE_TICK_COLOR, 0x000000);
FlexGlobals.topLevelApplication.styleManager.setStyleDeclaration("Spinner", newStyleDeclaration, true);
}
return true;
}
override public function styleChanged(styleProp:String):void {
if (styleProp == STYLE_TICK_COLOR) {
tickColorChanged = true;
invalidateDisplayList();
}
}
/**
* Begin the circular fading of the ticks.
*/
public function play():void {
if (! _isPlaying) {
fadeTimer = new Timer(speed / _numTicks, 0);
// addEventListener for the ticking going forward
fadeTimer.addEventListener(TimerEvent.TIMER, handleTicking);
fadeTimer.start();
_isPlaying = true;
}
}
/**
* Start the Tick at each Timer.
*/
public function handleTicking(e:TimerEvent):void {
var tickNum:int = int(fadeTimer.currentCount % _numTicks);
if (numChildren > tickNum) {
var tick:Tick = getChildAt(tickNum) as Tick;
tick.fade(fadeSpeed != 1 ? fadeSpeed : speed * 6 / 10);
}
}
/**
* Start the Tick at each Timer.
*/
public function handleUnloading(e:FlexEvent):void {
stop();
removeEventListener(FlexEvent.REMOVE, handleUnloading);
trace("Removing "+this.uid.toString());
}
/**
* Stop the spinning.
*/
public function stop():void {
if (fadeTimer != null && fadeTimer.running) {
_isPlaying = false;
fadeTimer.removeEventListener(TimerEvent.TIMER, handleTicking);
fadeTimer.stop();
}
}
/**
* The overall diameter of the spinner; also the height and width.
*/
[Bindable]
public function set size(value:Number):void {
if (value != _size) {
_size = value;
sizeChanged = true;
invalidateDisplayList();
invalidateSize();
}
}
public function get size():Number {
return _size;
}
/**
* The number of 'spokes' on the spinner.
*/
[Bindable]
public function set numTicks(value:int):void {
if (value != _numTicks) {
_numTicks = value;
numTicksChanged = true;
invalidateDisplayList();
}
}
public function get numTicks():int {
return _numTicks;
}
/**
* The width of the 'spokes' on the spinner.
*/
[Bindable]
public function set tickWidth(value:int):void {
if (value != _tickWidth) {
_tickWidth = value;
tickWidthChanged = true;
invalidateDisplayList();
}
}
public function get tickWidth():int {
return _tickWidth;
}
/**
* The duration (in milliseconds) that it takes for the spinner to make one revolution.
*/
[Bindable]
public function set speed(value:int):void {
if (value != _speed) {
_speed = value;
fadeTimer.stop();
fadeTimer.delay = value / _numTicks;
fadeTimer.start();
}
}
public function get speed():int {
return _speed;
}
public function get isPlaying():Boolean {
return _isPlaying;
}
}
}
Tick.as
package {
import flash.display.Sprite;
import mx.effects.Fade;
public class Tick extends Sprite {
private var tickFade:Fade = new Fade(this);
public function Tick(fromX:Number, fromY:Number, toX:Number, toY:Number, tickWidth:int, tickColor:uint) {
this.graphics.lineStyle(tickWidth, tickColor, 1.0, false, "normal", "rounded");
this.graphics.moveTo(fromX, fromY);
this.graphics.lineTo(toX, toY);
}
public function fade(duration:Number):void {
tickFade.alphaFrom = 1.0;
tickFade.alphaTo = 0.1;
tickFade.duration = duration;
tickFade.play();
}
}
}
You can hide the cursor rather than calling setBusyCursor on the CursorManager use http://www.igorcosta.com/flex3/doc/mx/managers/CursorManager.html#hideCursor() then just toggle the visibility of an overlay with your loading graphic.
You can use CursorManager.showCursor(); and CursorManager.removeBusyCursor(); to show and remove busy cursor.