Expanding root of tree in flex

This post is moved to http://srinichekuri.com/2014/02/21/expanding-root-of-tree-in-flex/

**********************************************************************

If you ever want to expand a tree (<mx:Tree>) immediately after setting dataprovider then follow this procedure. The challenge here is tree will not be ready to expand as it takes sometime for it to set data and render.

One common suggestion found on web is to use callLater() to call a function that has code to expand tree. This didn’t work for me (possibly because my code ran into same issue that I highlighted above). But following below method should definetly work as validateNow() method will make sure that all properties are set and will get the tree ready before expanding.

myTree.validateNow();
myTree.expandChildrenOf(_xml,true);
Advertisements

3 state checkbox for headerrenderer in datagrid

This post has been moved to http://srinichekuri.com/2011/05/20/3-state-checkbox-for-headerrenderer-in-datagrid/

**********************************************************************

Today I made by first contribution to Adobe Cook by posting about this topic. The idea on this topic started by showing my earlier post on gmail header to my colleague who also happens to be the owner of post in adobe cookbook that talks about 3State checkbox for TreeRenderer. He pointed out that the checkbox in the header can be threeState. Well thats a common problem but suprisingly this was not addressed in the online opensource community for Datagrid. I saw an opportunity and coded it.

Note:
There is a bug in my colleague’s post that it doesn’t take care of the initial state, I have taken care of that bug in my code.My code itself is inspired by my colleagues post.

The checkbox in headerRenderer will have three states:

  • All the rows in the datagrid are selected (‘select’ State).
  • None of the rows in the datagrid are selected(‘unselected’ State)
  • One or more rows (but not all) are selected(‘undecided’ State).

ScreenShots:
Change of state from ‘unselect’ state to ‘select’ state:

Change of state from ‘select’ state to ‘undecided’ state:

Code:

Here is the code for Main.mxml:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;s:Application xmlns:fx=&quot;http://ns.adobe.com/mxml/2009&quot;
				xmlns:s=&quot;library://ns.adobe.com/flex/spark&quot;
				xmlns:mx=&quot;library://ns.adobe.com/flex/mx&quot;
				creationComplete=&quot;creationCompleteHandler(event)&quot;&gt;

	&lt;fx:Script&gt;
		&lt;![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.FlexEvent;

			private var _dataProvider:ArrayCollection;
			[Bindable]
			public function set dataProvider(value:Object):void{
				_dataProvider=value as ArrayCollection
			}
			public function get dataProvider():Object{
				return _dataProvider;
			}

			protected function creationCompleteHandler(event:FlexEvent):void
			{
				dataProvider = new ArrayCollection([
					{checked:true, name:'Jim Smith'},
					{checked:false, name:'Yancy Williams'}]);
			}

		]]&gt;
	&lt;/fx:Script&gt;

	&lt;mx:DataGrid id=&quot;myDataGrid&quot; left=&quot;10&quot; top=&quot;10&quot; dataProvider=&quot;@{dataProvider}&quot;&gt;
		&lt;mx:columns&gt;
			&lt;mx:DataGridColumn textAlign=&quot;center&quot; dataField=&quot;checked&quot;
									   width=&quot;90&quot;
									   rendererIsEditor=&quot;true&quot;
									   sortable=&quot;false&quot;
									   itemRenderer=&quot;renderer.CheckboxItemRenderer&quot;
									   headerRenderer=&quot;renderer.ThreeStateCheckBoxHeaderRenderer&quot;
									   /&gt;
			&lt;mx:DataGridColumn headerText=&quot;Name&quot; dataField=&quot;name&quot; width=&quot;110&quot;/&gt;
		&lt;/mx:columns&gt;
	&lt;/mx:DataGrid&gt;

&lt;/s:Application&gt;

Code for headerRenderer

package renderer
{
	import flash.events.MouseEvent;

	import mx.collections.ArrayCollection;
	import mx.controls.DataGrid;
	import mx.controls.Image;
	import mx.controls.dataGridClasses.DataGridListData;
	import mx.controls.dataGridClasses.MXDataGridItemRenderer;

	import spark.components.CheckBox;

	/**
	 * HeaderRender with a Three State Checkbox.
	 * &lt;p&gt;Functionality:&lt;br&gt;
	 * 	  &lt;li&gt;Selecting the checkbox will select all the rows in the datagrid&lt;/li&gt;
	 * 	  &lt;li&gt;Unselecting the checkbox will unselect all the rows in the datagrid&lt;/li&gt;
	 *
	 * &lt;p&gt;The checkbox can be three states at any point:&lt;br&gt;
	 *    &lt;li&gt;select: This would mean that all the rows are selected&lt;/li&gt;
	 * 	  &lt;li&gt;unselect: This would mean that none of the rows are selected&lt;/li&gt;
	 *    &lt;li&gt;undecided: This would mean that one or more (but not all) of the rows are selected&lt;/li&gt;
	 *
	 * @author Srinivas Chekuri
	 *
	 */
	public class ThreeStateCheckBoxHeaderRenderer extends MXDataGridItemRenderer
	{
		protected var myCheckBox:CheckBox;
		protected var myImage:Image;
		private var imageWidth:Number 	= 9.5;
		private var imageHeight:Number 	= 9.5;
		private var inner:String 	= &quot;assets/inner.png&quot;;

		private const SELECT_STATE:String=&quot;select&quot;;
		private const UNSELECT_STATE:String=&quot;unselect&quot;;
		private const UNDECIDED_STATE:String=&quot;undecided&quot;;

		private var STATE:String = UNSELECT_STATE;

		/**
		 * Constuctor
		 *
		 */
		public function ThreeStateCheckBoxHeaderRenderer()
		{
			super();
		}

		/**
		 * overides the function &lt;code&gt;createChildren&lt;/code&gt; in the component lifecyle. This instantiates
		 * &lt;code&gt;myCheckBox&lt;/code&gt; and &lt;code&gt;myImage&lt;/code&gt;.
		 *
		 */
		override protected function createChildren():void{
			super.createChildren();
			myCheckBox = new CheckBox();
			myCheckBox.setStyle(&quot;horizontalCenter&quot;, &quot;0&quot;);
			myCheckBox.setStyle(&quot;verticalCenter&quot;, &quot;0&quot;);
			myCheckBox.addEventListener( MouseEvent.CLICK, checkBoxClickHandler );
			addElement(myCheckBox);
			myImage = new Image();
			myImage.source = inner;
			myImage.addEventListener( MouseEvent.CLICK, imageClickHandler );
			myImage.visible=false;
			addElement(myImage);
		}

		/**
		 * overides the function &lt;code&gt;updateDisplayList&lt;/code&gt; in the component lifecyle. This sets the
		 * display settings of &lt;code&gt;myCheckBox&lt;/code&gt; and &lt;code&gt;myImage&lt;/code&gt;. This also updates the state
		 * according to the selections made in the datagrid.
		 *
		 * @param unscaledWidth
		 * @param unscaledHeight
		 *
		 */
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth,unscaledHeight);

			myImage.x=myCheckBox.x+1.5;
			myImage.y=myCheckBox.y+5.5;

			myImage.width=imageWidth;
			myImage.height=imageHeight;

			if(areAllSelected()){
				STATE=SELECT_STATE;
			}else if(isAnyColumnSelected()){
				STATE=UNDECIDED_STATE;
			}else if(!isAnyColumnSelected()){
				STATE=UNSELECT_STATE;
			}
			checkState();
		}

		/**
		 * Makes adjustments to the &lt;code&gt;visible&lt;/code&gt; property of &lt;code&gt;myImage&lt;/code&gt; and
		 * &lt;code&gt;selected&lt;/code&gt; of &lt;code&gt;myCheckBox&lt;/code&gt; according to the state.
		 *
		 * @private
		 *
		 */
		private function checkState():void{
			if(STATE==SELECT_STATE){
				myImage.visible=false;
				myCheckBox.selected=true;
			}else if(STATE==UNSELECT_STATE){
				myImage.visible=false;
				myCheckBox.selected=false;
			}else{
				myImage.visible=true;
				myCheckBox.selected=false;
			}
		}

		/**
		 * Handler method for &lt;code&gt;MouseEvent.CLICK&lt;/code&gt; event on &lt;code&gt;myImage&lt;/code&gt;.
		 *
		 * @param event
		 *
		 */
		protected function imageClickHandler(event:MouseEvent):void{
			STATE=SELECT_STATE;
			checkState();
			selectAll();
		}

		/**
		 * Handler method for &lt;code&gt;MouseEvent.CLICK&lt;/code&gt; event on &lt;code&gt;myCheckBox&lt;/code&gt;.
		 *
		 * @param event
		 *
		 */
		protected function checkBoxClickHandler(event:MouseEvent):void{
			selectAll();
		}

		/**
		 * Selects all the rows in the datagrid.
		 *
		 */
		private function selectAll():void{
			var ac:ArrayCollection = DataGrid(DataGridListData(listData).owner).dataProvider as ArrayCollection;
			for each (var o:Object in ac){
				o.checked=myCheckBox.selected;
			}
			DataGrid(DataGridListData(listData).owner).dataProvider = ac;
		}

		/**
		 * Checks if all the rows are selected in the datagrid.
		 *
		 * @return Boolean: returns &lt;code&gt;true&lt;/code&gt; if all rows are selected else returns
		 * &lt;code&gt;false&lt;/code&gt;.
		 *
		 */
		private function areAllSelected():Boolean{
			var ac:ArrayCollection = DataGrid(DataGridListData(listData).owner).dataProvider as ArrayCollection;
			var b:Boolean=true;
			for each (var o:Object in ac){
				if(!o.checked){
					b=false;
					break;
				}
			}
			return b;
		}

		/**
		 * Checks to see if any one of the rows are selected.
		 *
		 * @return Boolean: return &lt;code&gt;true&lt;/code&gt; if any one the rows are selected.
		 *
		 */
		private function isAnyColumnSelected():Boolean{
			var ac:ArrayCollection = DataGrid(DataGridListData(listData).owner).dataProvider as ArrayCollection;
			var b:Boolean=false;
			for each (var o:Object in ac){
				if(o.checked){
					b=true;
					break;
				}
			}
			return b;
		}
	}
}

Code for itemRenderer:

&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?--&gt;

		&lt;![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.DataGrid;
			import mx.controls.dataGridClasses.DataGridListData;
			import mx.events.FlexEvent;

			/**
			 * Handler function for &lt;code&gt;MouseEvent.CLICK&lt;/code&gt; on checkbox.
			 *
			 * @param event MouseEvent
			 *
			 */
			public function clickHandler(event:MouseEvent):void{
				var o:Object = DataGrid(DataGridListData(listData).owner).selectedItem;
				o.checked=cb.selected;
				ArrayCollection(DataGrid(DataGridListData(listData).owner).dataProvider).setItemAt(o,DataGrid(DataGridListData(listData).owner).selectedIndex);
			}

			/**
			 * Overides &lt;code&gt;data&lt;/code&gt; parameter to set the &lt;code&gt;selected&lt;/code&gt; parameter of the
			 * checkbox.
			 *
			 * @param value Object
			 */
			override public function set data(value:Object):void{
				if(value!=null){
					super.data=value;
					cb.selected=value.checked;
				}
			}
		]]&gt;

You can download the code from my post in Adobe cook book.

Component Life Cycle in flex

This post has been moved to http://srinichekuri.com/2011/05/12/component-life-cycle-in-flex-for-a-button/

*********************************************************************

I was trying to experiment on some custom components and I ran into some rendering issues, so had to go back to refresh my memory about component life cycle. I thought of blogging about it. I have gone through many articles but found the post on Bill White’s blog and article in livedocs particularly interesting. I have summarized the lifecycle for those who would be interested in reading in bullets.

These are the series of methods that are called by Flex Framework for all components:
  1. As the component is going to be in a container, the parent property of the component will be referred to the container.
  2. Component’s Constructor:
    • Setup initial properties and do tasks that are not related to display of UI or not.
    • Don’t create child components or position items.
    • Constructor is called when component is created but the other lifecycle methods don’t occur until a component is added to a container.
  3. Gets the Style setting of the component.
  4. preInitialize event on the component
    • UIComponent is in a very basic stage with no children added at this time.
  5. createChildren() method is called.
    • Creates visual children of the component.
    • Flex doesn’t size or position them at this point because it doesn’t know how much screen space it has.
  6. initialize event is dispatched by the component –
    • All the component’s children are initialized.
  7. commitProperties() method
    • Is responsible to coordinate property modifications.
    • This is needed when there are multiple properties that should be set before you take action.
    • This is called when selectedIndex or selectedChild is called
    • Can be called by calling invalidateProperties(). Each time this method is called commitProperties() is scheduled to be called next time there is a screen repaint or refresh.
  8. measure() method is called only if explicit height and width is not set in the code
    • Flex calls this method to determine how much screen size a component requires.
    • This is not called when the height and width are explicitly set in the code as flex knows offhand how much screen size it will take.
    • This method sets these four properties (or you can set it)
      • measuredMinWidth
      • measuredMinHeight
      • measuredWidth
      • measuredHeight
    • LayoutManager also gets started at this point as layour manager starts with the outermost component and to size the outer most component it should know the size of its children.
    • Can be called by calling invalidateSize(). Each time this method is called measure() is scheduled to called next time there is a screen refresh.
  9. updateDisplayList() method is called
    • Is responsible for positioning of children. You can specify your children here
    • It is passed two paramters
      • unscaledWidth
      • unscaledHeight
    • Flex containers choose never to size one of their children smaller than minimum size but you can choose to override it.
    • Can be called by calling invalidateDisplayList(). Each time this method is called updateDisplayList() is scheduled to be called next time there is a screen refresh.
  10. Dispatches creationComplete event.
I wrote a very simple code to illustrate the lifecycle where a button is being painted. I have created a flex project called LifeCycle that will paint a Button. I have put in traces at various  stages.
LifeCycle.mxml
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;
&amp;lt;s:Application xmlns:fx=&amp;quot;http://ns.adobe.com/mxml/2009&amp;quot;
			   xmlns:s=&amp;quot;library://ns.adobe.com/flex/spark&amp;quot;
			   xmlns:mx=&amp;quot;library://ns.adobe.com/flex/mx&amp;quot; minWidth=&amp;quot;955&amp;quot; minHeight=&amp;quot;600&amp;quot; xmlns:local=&amp;quot;*&amp;quot;&amp;gt;
	&amp;lt;fx:Declarations&amp;gt;
		&amp;lt;!-- Place non-visual elements (e.g., services, value objects) here --&amp;gt;
	&amp;lt;/fx:Declarations&amp;gt;
	&amp;lt;local:MyButton/&amp;gt;
&amp;lt;/s:Application&amp;gt;
MyButton.as
package
{
	import mx.events.FlexEvent;
	import mx.events.PropertyChangeEvent;

	import spark.components.Button;

	public class MyButton extends Button
	{
		private var number:int=0;

		public function MyButton()
		{
			trace(&amp;quot;Constructor in Button -&amp;quot;+ number++);
			super();
			label=&amp;quot;test&amp;quot;;
			this.addEventListener(FlexEvent.PREINITIALIZE,preInitializeHandler);
			this.addEventListener(FlexEvent.INITIALIZE,initializeHandler);
			this.addEventListener(FlexEvent.CREATION_COMPLETE,creationCompleteHandler);
		}

		public function preInitializeHandler(e:FlexEvent):void{
			trace(&amp;quot;preInitialize event on Button - &amp;quot;+ number++);
		}

		public function initializeHandler(e:FlexEvent):void{
			trace(&amp;quot;Initialize event on Button - &amp;quot;+ number++);
		}

		public function creationCompleteHandler(e:FlexEvent):void{
			trace(&amp;quot;creationComplete event on Button - &amp;quot;+ number++);
		}

		override protected function createChildren():void{
			trace(&amp;quot;createChildren function in Button - &amp;quot;+ number++);
			super.createChildren();
		}

		override protected function commitProperties():void{
			trace(&amp;quot;CommitProperties function in Button - &amp;quot;+ number++);
			super.commitProperties();
		}

		override protected function measure():void{
			trace(&amp;quot;Measure function in Button - &amp;quot;+ number++);
			super.measure();
		}

		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			trace(&amp;quot;updateDisplayList function in Button - &amp;quot;+ number++);
			super.updateDisplayList(unscaledWidth,unscaledHeight);
		}

	}
}
Output in Console
Constructor in Button - 0
preInitialize event on Button - 1
createChildren function in Button - 2
Initialize event on Button - 3
CommitProperties function in Button - 4
Measure function in Button - 5
updateDisplayList function in Button - 6
creationComplete event on Button - 7

Google Mail like header component for Datagrid/AdvancedDatagrid in flex4

This post has been moved to http://srinichekuri.com/2011/04/23/google-mail-like-header-component-for-datagridadvanceddatagrid-in-flex4/

********************************************************************

I have tried to look around on web for a opensource component that simulates google mail(gmail) header like behavior in flex but couldn’t find one. As any other developer I have built my own component and am posting for the online community to make use of it.

You can have a look at the component here:

View Component

You can download the source code for this component here:

Download source code

As you can see I have customized the options that appears in the menu when you click on the arrow of the header. I will explain the behavior of the component before I take a deep drive into the code.

  • You can click on the arrow to select any one of the options given. I have put in 3 options:
    • All: Selects all the columns in the datagrid
    • None: Selects none of the rows and deselects rows if there are any already selected
    • Top 5: Select top 5 columns in the grid
  • Click on the checkbox on the header either select/deselects all the rows.

Here are few screen shots:

Click on the ‘Top5’ Option in the Header:

Component when top5 option is selected

 

Click on the CheckBox in the Header:
Component when the checkbox is clicked

Explanation of the code:
There are two main files that contribute as Datagrid’s header renderer:
DropDownButton:

package com.customization.controls
{
import com.customization.event.DropDownEvent;
import com.customization.skin.DropDownButtonSkin;

import flash.events.MouseEvent;

import mx.controls.Alert;
import mx.controls.Menu;
import mx.core.FlexGlobals;
import mx.core.mx_internal;
import mx.events.MenuEvent;
import mx.styles.CSSStyleDeclaration;
import mx.styles.IStyleManager2;
import mx.styles.StyleManager;

import spark.components.Button;

/**
 * Event dispatched when an option is selected from the dropdown menu.
 *
 * @eventType com.customization.event.DropDownEvent.OPTION_SELECTED
 */
[Event(name="optionSelected", type="com.customization.event.DropDownEvent")]
/**
 * DropDownButton extends <code>Button</code> component. When the button is clicked menu is
 * shown with options that can be selected.
 *
 * @author Srinivas Chekuri
 * @see DropDownSelector
 */
public class DropDownButton extends Button
{
/**
 * XML that defines the menu that should be displayed when clicked on the button.
 *
 * @default <xml></xml>
 */
public var myMenuData:XML=<xml></xml>;
private var menu:Menu;

public function DropDownButton()
{
super();
}

/**
 * overrides the initialize method of <code>mx.controls.Button</code>. EventListener are added to
 * <code>MouseEvent.CLICK</code> on this component and <code>MenuEvent.ITEM_CLICK</code> on Menu.
 * <code>skinClass</code> is set to <code>com.customization.skin.DropDownButtonSkin</code>.
 *
 */
override public function initialize():void {
super.initialize();
this.addEventListener(MouseEvent.CLICK,buttonMouseClickHandler);
this.setStyle("skinClass",com.customization.skin.DropDownButtonSkin);
menu = Menu.createMenu(null, myMenuData, false);
menu.labelField="@label";
menu.addEventListener(MenuEvent.ITEM_CLICK,menuClickHandler);
}

/**
 * Handler methos for mouse click on the button. This shows the menu.
 *
 * @param event MouseEvent
 *
 */
private function buttonMouseClickHandler(event:MouseEvent):void{
menu.show(this.x,20);
}

/**
 * Handler method that will be called when option is selected in the menu by a mouse click.
 *
 * @param event MenuEvent
 *
 */
private function menuClickHandler(event:MenuEvent):void{
var dropDownButtonEvent:DropDownEvent = new DropDownEvent(DropDownEvent.OPTION_SELECTED);
dropDownButtonEvent.selectedOption=event.item.@label;
dispatchEvent(dropDownButtonEvent);
}
}
}

DropDownSelector:

package com.customization.controls
{
import com.customization.event.DropDownEvent;

import flash.events.MouseEvent;

import mx.containers.HBox;
import mx.controls.Alert;

import spark.components.CheckBox;
import spark.components.HGroup;

/**
 * Event that is thrown when the checkbox is checked or unchecked.
 *
 * @eventType com.customization.event.DropDownEvent.CHECKBOX_STATUS_CHANGE
 */
[Event(name="checkBoxStatusChange", type="com.customization.event.DropDownEvent")]
/**
 * DropDownSelector is a class that hosts a google mail like header component that will allow us
 * to select the options from the drop down menu.
 *
 * @author Srinivas Chekuri
 * @see DropDownButton
 */
public class DropDownSelector extends HBox
{
private var checkBox:CheckBox;
private var button:DropDownButton;
private var myMenuData:XML=<XML>
<menuitem label="All"/>
<menuitem label="None"/>
<menuitem label="Top 5"/>
</XML>;
/**
 * Constructor.
 *
 */
public function DropDownSelector()
{
super();
intializeView();
}

/**
 * Method that initialize the views by instantiating the checkbox and the DropDownButton component.
 * This also adds event listeners to clicks on both these components.
 *
 * @private
 */
private function intializeView():void{
this.setStyle("horizontalGap",0);
this.setStyle("verticalGap",0);
this.setStyle("horizontalAlign","center");
this.setStyle("verticalAlign","center");
//Initializing CheckBox
checkBox = new CheckBox();
checkBox.horizontalCenter=0;
checkBox.verticalCenter=0;
checkBox.addEventListener(MouseEvent.CLICK,checkboxMouseClickHandler);
checkBox.horizontalCenter=0;
checkBox.verticalCenter=0;

//Initializing DropDownButton
button = new DropDownButton();
button.myMenuData=myMenuData;
button.width=15;
button.horizontalCenter=0
button.verticalCenter=0;
button.addEventListener(DropDownEvent.OPTION_SELECTED,optionSelectHandler);

this.addChild(checkBox);
this.addChild(button);
}

/**
 * Handler method when checkbox is clicked. When ever this method is called
 * <code>DropDownEvent.CHECKBOX_STATUS_CHANGE</code>id dispatched.
 *
 * @param e MouseEvent dispatched.
 * @eventType com.customization.event.DropDownEvent.CHECKBOX_STATUS_CHANGE
 */
protected function checkboxMouseClickHandler(e:MouseEvent):void{
var event:DropDownEvent = new DropDownEvent(DropDownEvent.CHECKBOX_STATUS_CHANGE);
event.checkBoxSelectStatus=e.target.selected;
dispatchEvent(event);
}

/**
 * Handler method when a option gets selected from dropdown menu.
 *
 * @param event DropDownEvent dispatched.
 *
 */
protected function optionSelectHandler(event:DropDownEvent):void{
if(event.selectedOption!=null){
checkBox.selected=true;
}
}
}
}

Apart from this you might also want to look at DropDownButtonSkin.mxml for skinning and Customization.mxml on how the events were handeled.