Monday, January 26, 2009

Flex Advanced Data Grid collapses on refresh

I was having this problem with my Advanced Data Grid. Every time the data refreshed the whole thing collapsed, which isn't a good thing when users have drilled down 3 levels.

I know this is probably a bad way of doing this, but it is the only way I could get to work. All the examples I've seen of this say to just assign the saved openNodes Object to the openNodes property, like this...

myOpenNodes = new Object();
myOpenNodes = IHierarchicalCollectionView(myADG.dataProvider).openNodes

//refresh here

IHierarchicalCollectionView(myADG.dataProvider).openNodes = myOpenNodes;

but that doesn't work!!

Here is what I did:

On the refresh method set the following variables (these variables need to be accessible from all methods inside the component, so put them in your model or make them global, whatever). These variables hold the state of the ADG before the refresh.

//Object holding all currently open nodes
myOpenNodes = new Object();
myOpenNodes = IHierarchicalCollectionView(myADG.dataProvider).openNodes

lastSelectedItem = myADG.selectedItem;

lastScrollPosition = myADG.verticalScrollPosition;


This method loops through every item in the advanced data grid up to the depth you provide (here it is to the depth of 4). Then any time it finds an object that is also in the myOpenNodes object it adds to to an array that then is assigned to the ADG.openNodes property. In this example the property of the grid item I am using to compare is called "Group".

private function selectLastADGItem(evt:Event):void
{
//Array to assign to openNodes property
var savedNodesArray:Array = [];

for(var i:int=1; i<4; i++)
{
var dataCursor:IHierarchicalCollectionViewCursor = myADG.dataProvider.createCursor();

while (dataCursor.current)
{

for each ( var item:Object in myOpenNodes )
{
//Check if current item is in openNodes object
if ( item.Group == dataCursor.current.Group )
{
savedNodesArray.push(dataCursor.current);
}

//Check if current item is the last selected item
if ( item.Group == dataCursor.current.Group )
{
myADG.selectedItem = dataCursor.current;
}
}

dataCursor.moveNext();
}

myADG.validateNow();

//Assign new array to openNodes property IHierarchicalCollectionView(myADG.dataProvider).openNodes = savedNodesArray;

myADG.dataProvider.refresh();
}

//Move vertical scrollbar to last position
myADG.verticalScrollPosition = lastScrollPosition;

}

Wednesday, January 21, 2009

flex: Dictionary Class

Today I was introduced to Dictionaries. No, not the kind that you look up the definitions of words, the Flex code kind. The Class. I was having trouble with this Grid that I am working on, the labels are hard coded in the grid (because of formatting and ordering requirements), and there could be anywhere from 1 to 8 data values that go along with that label. At first I had ArrayCollections with a name property (the label) and the values of each data point, and then I was using methods with return values to get the value I wanted back by looping through the ArrayCollection and looking for the name. This seemed really heavy. I asked a co-worker who is more versed in ActionScript than I am, and he told me about dictionaries. With these you can look up an object via another object rather than an index.

Here are some code snips!

First declare your dictionary object!

private var myDict:Dictionary = new Dictionary();


Then, in a method or event somewhere, populate the Dictionary object with the data. (note - if you're populating the data in the same place, then do it on a preInitialize method).

private function preInit():void
{
myDict["banana"] = {w:2.5, h:8, color:'yellow'};
myDict["apple"] = {w:3.5, h:3.5, color:'red'};
myDict["orange"] = {w:3, h:3, color:'orange'};
}


And to use it.

trace( myDict["apple"].color );
//This will output: red

Tuesday, January 20, 2009

Flex PieChart - fill colors

I think this may only work in Flex 3.

My goal was to get the pie charts and line charts on this one view of the dashboard all to sync up color-wise. (The line chart stuff can be found in an earlier post.)

This will also only work if you know what you'll be getting back so far as items.

Lets say I know I will be getting back the following and I know that I want each slice to have a specific coordinating color.

Hearts - Red
Spades - Blue
Clovers - Green
Stars - Yellow

I would create something called a Fill Function. This is called in the PieSeries tag. Your fill function would consist of a switch statement checking the current item, and assigning its fill. In my example code below I use a Radial Gradient, but you can use SolidColor as well.


private function myFillFunction(item:ChartItem, index:Number):IFill
{
var curItem:PieSeriesItem = PieSeriesItem(item);
var fill:RadialGradient = new RadialGradient();
var g1:GradientEntry = new GradientEntry();
var g2:GradientEntry = new GradientEntry();
g1.alpha = 1.0;
g2.alpha = 1.0;

switch ( curItem.item.yourProperty )
{
case "Hearts":
//Choose uint color values eg: 0xFFFFFF for white etc.
g1.color = LightRed;
g2.color = DarkRed;
break;
case "Spades":
g1.color = LightBlue;
g2.color = DarkBlue;
break;
case "Clovers":
g1.color = LightGreen;
g2.color = DarkGreen;
break;
case "Stars":
g1.color = LightYellow;
g2.color = DarkYellow;
break;
default:
g1.color = LightGray;
g2.color = DarkGray;
break;
}

fill.entries = [g1,g2];
return fill;
}

...
<mx:PieSeries
fillFunction="myFillFunction">
...

Thursday, January 15, 2009

Flex Super Tab Navigator

So I have this dashboard project I'm working on. What I am trying to do is have a TabNavigator that is about half the screen, but give it a maximize button, so that if clicked it fills the whole screen. Sounds easy enough, I've done it with a Panel, but unfortunately it seemed a little too tricky for me and my high design : moderate coding skills. So to Google I went.

You know what annoys me? People who blog about this kind of thing, show the awesome component they created, and don't share even a bit of code. That's what I found. Ugh. I did however discover that flexLib has something called a SuperTabNavigator. The SuperTabNavigator has a close button on each tab, so I figured, maybe I can extend this, better than starting from scratch. It wasn't even as difficult as extending it. It is actually pretty easy to customize. Yay for flexLib.

Here are some actual CODE EXAMPLES of what I did.

First you need the flexLib library. http://code.google.com/p/flexlib/downloads/list, add it by referencing the swc file in your project's library path, like you would Cairngorm.

First here's the SuperTabNavigator, which I placed inside a Canvas.

...
<code:SuperTabNavigator id="mySuperTabNav"
width="100%" height="50%"
y={this.height/2}
popUpButtonPolicy="off"
closePolicy="close_always"
tabClose="maxClick(event)">
<mx:VBox label="testing"/>
</code:SuperTabNavigator>
...


Here is the code for the maxClick method.

...
private function maxClick(event:SuperTabEvent):void
{
//This stops the default close action from happening
event.preventDefault();
//Make sure the tab that was clicked is the one that is selected
event.currentTarget.selectedIndex = event.tabIndex;

//Put your max/restore tab code here!
/*example: mySuperTabNav.y = 0;
mySuperTabNav.percentHeight = 100;*/
}
...


Now... it wouldn't be very user friendly if clicking a close button maximizes the tab navigator, now would it? So through CSS we need to change the icon/button on the tab. Now this is kind of odd, I found this here: http://groups.google.com/group/flexlib/browse_thread/thread/d94196bc6510c49c
.
...
SuperTabNavigator
{
tab-style-name: customTabMax;
}
.customTabMax
{
tab-close-button-style-name: maxIconButton;
}
.maxIconButton
{
upSkin:Embed(source="this/is/image/path.png");
overSkin:Embed(source="this/is/image/path.png");
downSkin:Embed(source="this/is/image/path.png");
disabled-skin:Embed(source="this/is/image/path.png");
}
...


Here's a link to the documentation:
http://flexlib.googlecode.com/svn/trunk/docs/flexlib/containers/SuperTabNavigator.html

See my Sample (right click to view source)

Monday, January 5, 2009

datatipfunction in a linechart

To customize the datatip in a linechart in flex you need to use the datatipfunction property that refers to a function. This function must return a string and take a parameter of type HitData. To get the current VALUE of the line point you're mousing over you need to use LineSeriesItem(hitData.chartItem).yValue - I found this difficult to find out at first.

Here's an example:

private function lcDataTipFunction(hitData:HitData):String
{
var s:String;
s = LineSeries(hitData.element).displayName + "\n";
s += LineSeriesItem(hitData.chartItem).yValue + "\n";
s += hitData.item.Day + " at " + hitData.item.Time;
return s;
}