Hiding row or column or both in a datagrid

3 08 2009

Sometimes, you come across a situation wherein you need to hide a particular row or column or both of a datagrid. Now we have three cases in hand.

  • Hiding a column
  • Hiding a row
  • Hiding row and column

Let us see each of them in detail.
1) Hiding a column: This is the most easiest one of them. You can just give, DataGridColumn’s visibility to false and there you go. The mentioned column is no longer visible.

2) Hiding a row: Now comes the twisted part. You cannot mention like, DataGridRow’s visibility as it is not available neither you cannot mention a particular row index or something similar. Here you will have to use the ‘ListCollectionView‘ class and the filter funtion available in it. While creating the ‘ListCollectionView‘ class, the underlying dataprovider that is used for datagrid must be used. Then apply the filter function to it, which filters out the data according to the conditions. Now, use the ‘refresh()‘ to refresh the ListCollection. Finally, use this ‘ListCollectionView’ class as the dataprovider for your datagrid.

3) Hiding row and column: It is a combination of both the steps mentioned above. But make sure that you have applied the hiding of rows first and then hiding of columns as hiding of columns first and rows second will not work. Try it and you will know why :)

Now, combining all these, let us see an example. As you can see while running this example, you can either hide a column or a row or a combination of both, according to particular conditions.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    creationComplete="initApp()">
    <mx:Script>
        <![CDATA[
        	import mx.controls.Alert;
        	import mx.rpc.events.FaultEvent;
        	import mx.controls.dataGridClasses.DataGridColumn;

        	import mx.utils.ObjectUtil;
            import mx.collections.Sort;
            import mx.collections.ListCollectionView;
            import mx.collections.IViewCursor;
            import mx.collections.ArrayCollection;
            import mx.rpc.events.ResultEvent;

            [Bindable]
            private var itemsRating:ListCollectionView;
            [Bindable]
            private var itemHonda:ListCollectionView;
            [Bindable]
            private var itemHondaBMW:ListCollectionView;

            [Bindable]
            private var itemVehicles:ArrayCollection;

            /**
            * establishing connection between the xml and application
            * the httpservice is invoked using send method
            */

            private function initApp():void
            {
                itemConn.send();
            }

            /**
            * to handle the result event of the http service call
            *
            */

            private function resultHandler(event:ResultEvent):void
            {
            	itemVehicles = itemConn.lastResult.items.item as ArrayCollection;
            }

            /**
            * to handle the fault event of the http service call
            * here, when the xml is not present
            */

            private function faultHandler(event:FaultEvent):void
            {
            	Alert.show("Values not retrieved","Error");
            }

            /**
            * when rating only is selected, row
            */

            private function handleRating():void
            {
                itemsRating = new ListCollectionView(itemVehicles);
                itemsRating.filterFunction = calcRating;
                itemsRating.refresh();
                itemDG.dataProvider = itemsRating;
            }

            /**
            * filtering out the values for rating less than eight
            */

            private function calcRating(value:Object):Boolean
            {
            	return Number(value.Rating) < 8;
            }

            /**
            * to display honda vehicles only, row
            */

            private function hondaOnly():void
            {
            	itemHonda = new ListCollectionView(itemVehicles);
            	itemHonda.filterFunction = calcHonda;
            	itemHonda.refresh();
            	itemDG.dataProvider = itemHonda;
            }

            /**
            * filtering out the values based on honda
            */

            private function calcHonda(value:Object):Boolean
            {
            	return String(value.Name).toUpperCase() == "HONDA";
            }

            /**
            * for honda n bmw suv vehicles, row
            */

            private function hondaBMW():void
            {
            	itemHondaBMW = new ListCollectionView(itemVehicles);
            	itemHondaBMW.filterFunction = calcHondaBMW;
            	itemHondaBMW.refresh();
            	itemDG.dataProvider = itemHondaBMW;
            }

            /**
            * filtering out honda n bmw suv vehicles
            */

            private function calcHondaBMW(value:Object):Boolean
            {
            	return (String(value.Name).toUpperCase() == "HONDA"||String(value.Name).toUpperCase() == "BMW")&&(String(value.Segment).toUpperCase() == "SUV/CROSSOVER");
            }

            /**
            * hiding the name column
            */

            private function nameHide():void
            {
            	//if this is commented, then earlier hidden columns will still be hidden
            	itemDG.dataProvider = itemVehicles;

            	var totColumns:Array = itemDG.columns;
            	var dgc:DataGridColumn;

            	//tracing out the datagrid column having the title name
            	for (var i:int = 0; i < totColumns.length; i++)
            	{
            		if(totColumns[i].dataField.toUpperCase() == "NAME")
            		{
            			dgc = totColumns[i];
            			break;
            		}
            	}
            	dgc.visible = false;
            }

            /**
            * hiding the segment column
            */

            private function segmentHide():void
            {
            	//if this is commented, then earlier hidden columns will still be hidden
            	itemDG.dataProvider = itemVehicles;

            	var totColumns:Array = itemDG.columns;
            	var dgc:DataGridColumn;

            	//tracing out the datagrid column having the title segment
            	for (var i:int = 0; i < totColumns.length; i++)
            	{
            		if(totColumns[i].dataField.toUpperCase() == "SEGMENT")
            		{
            			dgc = totColumns[i];
            			break;
            		}
            	}
            	dgc.visible = false;
            }

            /**
            * hiding the rating n price columns
            */

            private function ratePriceHide():void
            {
            	//if this is commented, then earlier hidden columns will still be hidden
            	itemDG.dataProvider = itemVehicles;

            	var totColumns:Array = itemDG.columns;
            	var dgc:DataGridColumn;

            	//tracing out the datagrid column having the title price or rating
            	for (var i:int = 0; i < totColumns.length; i++)
            	{
            		if(totColumns[i].dataField.toUpperCase() == "PRICE" || totColumns[i].dataField.toUpperCase() == "RATING")
            		{
            			dgc = totColumns[i];
            			dgc.visible = false;
            		}
            	}
            }

            /**
            * showing the name n price column of passenger row
            */

            private function NPP():void
            {
            	//if this is commented, then earlier hidden columns will still be hidden
            	itemDG.dataProvider = itemVehicles;

            	//refresh the dataprovider of datagrid to reflect only the rows of passenger segment
            	itemHonda = new ListCollectionView(itemVehicles);
            	itemHonda.filterFunction = function(value:Object):Boolean{return String(value.Segment).toUpperCase() == "PASSENGER";};
            	itemHonda.refresh();
            	itemDG.dataProvider = itemHonda;

            	//hide the model n rating columns
            	var totColumns:Array = itemDG.columns;
            	var dgc:DataGridColumn;
            	for (var i:int = 0; i < totColumns.length; i++)
            	{
            		if(totColumns[i].dataField.toUpperCase() == "MODEL" || totColumns[i].dataField.toUpperCase() == "RATING")
            		{
            			dgc = totColumns[i];
            			dgc.visible = false;
            		}
            	}

            }
		]]>
    </mx:Script>

     <mx:HTTPService id="itemConn" url="items.xml" useProxy="false"
     	result="resultHandler(event)"
     	fault="faultHandler(event)"/>

    <mx:Panel layout="absolute" left="5" top="5" right="5" bottom="5" title="Show / Hide rows & columns">

		<mx:DataGrid id="itemDG" dataProvider="{itemVehicles}" editable="false" width="100%" height="100%"/>

		<mx:ControlBar>
			<mx:HBox horizontalGap="100">
				<mx:VBox>
					<mx:Label text="Hide / Show rows"/>
					<mx:LinkButton label="All rows" click="{itemDG.dataProvider = itemVehicles}"/>
            		<mx:LinkButton label="Rating < 8" click="handleRating()"/>
            		<mx:LinkButton label="Honda only" click="hondaOnly()"/>
            		<mx:LinkButton label="Honda & BMW SUV only" click="hondaBMW()"/>
				</mx:VBox>
				<mx:VBox>
					<mx:Label text="Hide / Show columns"/>
					<mx:LinkButton label="All columns" click="{itemDG.dataProvider = itemVehicles}"/>
            		<mx:LinkButton label="Hide Name" click="nameHide()"/>
            		<mx:LinkButton label="Hide Segment" click="segmentHide()"/>
            		<mx:LinkButton label="Hide Price & Rating" click="ratePriceHide()"/>
				</mx:VBox>
				<mx:VBox>
					<mx:Label text="Hide / Show Both"/>
					<mx:LinkButton label="All rows & columns" click="{itemDG.dataProvider = itemVehicles}"/>
            		<mx:LinkButton label="Show only Name, Price of Passenger segment" click="NPP()"/>
				</mx:VBox>
			</mx:HBox>
        </mx:ControlBar>
    </mx:Panel>

</mx:Application>

And here is the items.xml

<?xml version="1.0" encoding="UTF-8"?>
<items>
	<item Name="Ford" Segment="Passenger" Model="Fusion" Price="24,500" Rating="9.4"/>
	<item Name="Ford" Segment="Passenger" Model="Taurus X" Price="31,000" Rating="7.5"/>
	<item Name="Ford" Segment="Sports" Model="Mustang" Price="28,000" Rating="9.2"/>
	<item Name="Ford" Segment="Sports" Model="Shelby GT500" Price="50,000" Rating="9.3"/>
	<item Name="Ford" Segment="SUV/Crossover" Model="Escape" Price="24,000" Rating="8.5"/>
	<item Name="Ford" Segment="SUV/Crossover" Model="Expedition" Price="40,500" Rating="7.5"/>
	<item Name="Ford" Segment="Pick UpTruck" Model="Explorer Sport" Price="31,500" Rating="6.9"/>
	<item Name="Ford" Segment="Pick Up Truck" Model="F-450 Super duty" Price="47,500" Rating="8.2"/>

	<item Name="Honda" Segment="Passenger" Model="Civic" Price="16,000" Rating="9.0"/>
	<item Name="Honda" Segment="Passenger" Model="Civic hybrid" Price="24,500" Rating="8.2"/>
	<item Name="Honda" Segment="Sports" Model="S2000" Price="36,500" Rating="9.0"/>
	<item Name="Honda" Segment="SUV/Crossover" Model="CR-V" Price="25,000" Rating="8.9"/>
	<item Name="Honda" Segment="SUV/Crossover" Model="Pilot" Price="33,000" Rating="9.4"/>
	<item Name="Honda" Segment="Pick UpTruck" Model="Ridgeline" Price="32,000" Rating="8.0"/>

	<item Name="Audi" Segment="Passenger" Model="TT" Price="40,000" Rating="8.5"/>
	<item Name="Audi" Segment="Passenger" Model="S4 Avant" Price="50,000" Rating="9.5"/>
	<item Name="Audi" Segment="Luxury" Model="A8" Price="85,000" Rating="9.0"/>
	<item Name="Audi" Segment="Luxury" Model="S8" Price="96,000" Rating="9"/>
	<item Name="Audi" Segment="Sports" Model="TT" Price="40,000" Rating="8.5"/>
	<item Name="Audi" Segment="Sports" Model="R8" Price="118,000" Rating="9.2"/>
	<item Name="Audi" Segment="SUV/Crossover" Model="Q5" Price="37,000" Rating="9.0"/>
	<item Name="Audi" Segment="SUV/Crossover" Model="Q7" Price="52,000" Rating="8.0"/>

	<item Name="BMW" Segment="Passenger" Model="1 series" Price="35,000" Rating="6.8"/>
	<item Name="BMW" Segment="Passenger" Model="7 series" Price="82,000" Rating="8.9"/>
	<item Name="BMW" Segment="Luxury" Model="M3" Price="60,000" Rating="9.0"/>
	<item Name="BMW" Segment="Luxury" Model="M6" Price="105,500" Rating="8.5"/>
	<item Name="BMW" Segment="Sports" Model="Z4" Price="47,500" Rating="10"/>
	<item Name="BMW" Segment="Sports" Model="M6" Price="105,500" Rating="8.5"/>
	<item Name="BMW" Segment="SUV/Crossover" Model="X5" Price="52,500" Rating="9.9"/>
	<item Name="BMW" Segment="SUV/Crossover" Model="X6" Price="60,500" Rating="9.3"/>

</items>




Style Programming

23 07 2008

Consider a scenario in which the requirement is just to create a Window with a Panel that can resize itself by clicking on the button +/- that is located in the top-right corner of the panel. One click should minimize the panel’s height to 20 pixels, and a subsequent one should maximize to 100 pixels. How efficiently do you write the code?

There are many ways to do so.

First method:- With the usage of packages

package
{
	import mx.containers.Panel;

	public class UpDownPanel extends Panel
	{
		private  var isPanelMinimized:Boolean;//whether panel is minimised or maximized

		public function UpDownPanel()
		{
			super();
		}

		//getter & setter for the states
		public function get minimized():Boolean{
			return isPanelMinimized;
		}
		public function set minimized(state:Boolean):void{
			isPanelMinimized=state;
		}
		//resizing function
		public function resizeMe():void{
			if (minimized){
				minimized=false;
				height=maxHeight;
			} else {
				minimized=true;
				height=minHeight;
			}
		}
	}
}

And the mxml is…

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:customPanel="*">

	<customPanel:UpDownPanel id="minmaxPanel" title="Height adjuster"
		width="100%" height="100" headerHeight="20" minHeight="20" maxHeight="100"/>

	<mx:HBox width="100%" horizontalAlign="right" paddingRight="2">
		<mx:Label id="minButton" text="-"
			fontSize="16" fontWeight="bold"
			width="20" height="17"
			click="resizePanel(minmaxPanel)"/>
	</mx:HBox>

	<mx:Script>
		<![CDATA[
			private function resizePanel(value:UpDownPanel):void{
				if (value.minimized){
					minButton.text="-";
					value.resizeMe();
               } else {
					minButton.text="+";
					value.resizeMe();
               }
			}
		]]>
	</mx:Script>

</mx:Application>

Good amount of coding, isn’t it?

Second method:- Simple with a few lines of code only!

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

	<mx:Panel id="minmaxPanel" title="Height adjuster"
		width="100%" height="100" headerHeight="20"
		layout="absolute"/>

	<mx:HBox width="100%" horizontalAlign="right" paddingRight="2">
		<mx:Label id="minButton" text="-"
			fontSize="16" fontWeight="bold"
			width="20" height="17"
			click="{if (minButton.text=='+')
					{
						minButton.text='-';
						minmaxPanel.height=100;
					}else
					{
						minButton.text='+';
						minmaxPanel.height=20;
					}
				   }"/>
	</mx:HBox>

</mx:Application>

That was a nice piece….read the rest too.

Third method:- Using states.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns="*">

	<mx:states>
		<mx:State name="minimized">
			<mx:SetProperty target="{minmaxPanel}" name="height" value="20"/>
			<mx:SetProperty target="{minButton}" name="text" value="+"/>
			<mx:SetEventHandler target="{minButton}" name="click" handler="{this.currentState = ''}"/>
		</mx:State>
	</mx:states>

	<mx:Panel id="minmaxPanel" title="Height adjuster"
		width="100%" height="100" headerHeight="20"
		layout="absolute"/>

	<mx:HBox width="100%" horizontalAlign="right" paddingRight="2">
		<mx:Label id="minButton" text="-"
			fontSize="16" fontWeight="bold"
			width="20" height="17"
			click="{this.currentState = 'minimized'}"/>
	</mx:HBox>

</mx:Application>

What if more controls are there on each state?

Fourth method:- with dynamic classes.

package
{
	import mx.containers.Panel;

	public dynamic class UpDownPanel extends Panel
	{
	}
}

And the mxml is…

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:customPanel="*">

	<customPanel:UpDownPanel id="minmaxPanel" title="Height adjuster"
		width="100%" height="100" headerHeight="20" minHeight="20" maxHeight="100"/>

	<mx:HBox width="100%" horizontalAlign="right" paddingRight="2">
		<mx:Label id="minButton" text="-"
			fontSize="16" fontWeight="bold"
			width="20" height="17"
			click="resizePanel(minmaxPanel)"/>
	</mx:HBox>

	<mx:Script>
		<![CDATA[
			private function resizePanel(value:UpDownPanel):void{
				if (value.minimized){
					minButton.text="-";
					value.minimized = false;
					value.height = value.maxHeight;
               } else {
					minButton.text="+";
					value.minimized = true;
					value.height = value.minHeight;
               }
			}
		]]>
	</mx:Script>

</mx:Application>

Now, that piece of code is extensible, right? Wondering what is dynamic classes? Look here.

Fifth method:- A proper MXML with scripting.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns="*">

	<mx:Component className="UpDownPanel">
		<mx:Panel>
			<mx:Script>
				<![CDATA[
					[Bindable]
					public var isMinimised:Boolean = false;
				]]>
			</mx:Script>
		</mx:Panel>
	</mx:Component>

	<UpDownPanel id="minmaxPanel" title="Height adjuster"
		isMinimised = "false"
		width="100%" headerHeight="20" minHeight="20" maxHeight="100"
		height="{minmaxPanel.isMinimised?minmaxPanel.minHeight:minmaxPanel.maxHeight}"/>

	<mx:HBox width="100%" horizontalAlign="right" paddingRight="2">
		<mx:Label id="minButton" text="-"
			fontSize="16" fontWeight="bold"
			width="20" height="17"
			click="{minmaxPanel.isMinimised=!minmaxPanel.isMinimised}"/>
	</mx:HBox>

</mx:Application>

Finally it was simple, right?

Which of these methods do you think is good in terms of performance/stability/readability/etc? Or if you have another way of doing it, do post it in comments!
Also there are advantages/disadvantages for each method. For example, while using states, it may not become manageable if there are lots of controls in each of the state. Like wise if you can find any, please put it in comments.





Global Variables within Flex Applications

6 06 2008

Just define a public variable within your main application class, like,

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
		<![CDATA[
			public var foo:String = "bar";
		]]>
	</mx:Script>
</mx:Application>

You can then access that variable from anywhere within the Flex application via:
Application.application.foo