Column charts with ticks

After having some issues with the Flex Forum due to spamming (not me), I went back on to answer a few quick questions. A question about creating tick lines between the interval lines on a column chart was followed up with setting the number of tick lines between the intervals. The first part of adding tick lines is fairly straight forward. When you create a ColumnChart and add LinearAxis, you can set the minorInterval value, which creates the ticks between the main intervals. After that is set, you can add an AxisRenderer to the verticalAxisRender to adjust what the tick looks like.

<mx:verticalAxisRenderers>
	<mx:AxisRenderer axis="{countAxis}" minorTickPlacement="inside" minorTickStroke="{s1}" minorTickLength="330" />
</mx:verticalAxisRenderers>

As part of the question, the user wanted to create grid lines, which to me was to stretch the tick line across the entire chart. In order to have the tick lines only show up on the chart, you need to set the placement to “inside” and set the length. Unfortunately, you can’t simply get the width of the chart and set the length of the tick line, because the chart also includes any legends and interval numbers. Also, the tick line is white, so if you want it to match the grid lines, you need to create a stroke and set that value.

Finally, the user asked if the number of tick lines could be manipulated. What I found was that the chart would take the main interval value and divided it by the minor interval value in order to show the number of ticks, including the tick at the interval value. So, I came up with the simple formula to determine the minorInterval value based on a users selection. I added the value to a change function for a droplist of 1-9. The value 0 doesn’t work, because once you set it to show the minorInterval, it will always show something.

Minor Interval Value = Major Interval Value / (Viewable Tick Count + 1)

protected function tickViewList_changeHandler(event:IndexChangeEvent):void
{
	var tickCount:int = tickViewList.selectedItem;
	var tickView:Number = countAxis.interval / (tickCount + 1);
	countAxis.minorInterval = tickView;
}

View Source

Color name to uint value

Filing under the “why didn’t I find this before” folder. On the Adobe Flex forums, there was a question about changing the color value of a button based on a switch statement. The user was trying to change the value to “Red”, “Yellow” or “Green”. In the button, the type for color is uint value, so the input needs to be something like “0×990000″ for “Red”. I usually just insert in the uint value for a color and not the name, since I usually have to change it. But, this question on the forum got me thinking and searching for an answer. The simple answer is the IStyleManager2, which allows you to get the uint value of a color name. Presto!

Here’s the reply to the user’s question that I have been attempting to post to the forum.


First set a declaration of the variable. I am setting the default value to Black in uint value.

<fx:Declarations> <fx:uint id="batteryStatusColor">0x000000</fx:uint></fx:Declarations>

This will allow you to bind it to your button parameter.

<s:Button id="Battery" x="791" y="513" width="124" height="46" 
label="Battery" fontSize="20" color="{batteryStatusColor}" />

Then, in your function, make sure you convert the String value of the color to a uint

private function changeBatteryStatusColor(batteryStatus:int):void {
	var tmpColorString:String = "Black";
	switch(batteryStatus)
	{
		case 1:                                                                                                                 
			tmpColorString = "Red";                                                                                          
			break;
		case 2:                                                                                                                 
			tmpColorString = "Green";
			break;
		default:                                                                                                                 
			tmpColorString = "Yellow";                                                                                                    
			break;                   	
	}   
	
	var newStyleManger:IStyleManager2 = StyleManager.getStyleManager(this.moduleFactory);	
	batteryStatusColor = newStyleManger.getColorName(tmpColorString);
}

The key part is the IStyleManager call that converts the String value of the color to a uint.

Continuos Vibrate and Music Alert until Confirmation : update

I had recently thought I solved my issue with creating a continuous vibrate and music alert for the BlackBerry application that I created for work. Unfortunately, when I put the fix on to the phone, the volume level of the music was very low, even though I had the volume set to 100%. So, it was back to the drawing board to try and figure out how to get this working. The main issue was determining if the music had finished and then starting it again until the user pressed a key.

After messing around with NotificationsEngineListener, Event and the Notifications Demo for what seemed like forever and continuing to get thread errors and other issues, it was a simple step back and regroup that helped me see the light. Instead of trying to kick off an event and a listener for the Notification, all I had to do was immediately kick off the Notification.

NotificationsManager.triggerImmediateEvent(bbhAttribute.NOTIFICATION_MESSAGEPAGE, 0, null, null);

I was already checking to see when the music had finished to determine if I needed to stop or continue. So, the simple step was to put the triggerImmediateEvent in the check.

public void audioDone(int reason)
{
	System.out.println("audioDone function");
	
	switch(reason) {
		case REASON_KEY_PRESSED:
			//System.out.println("Key Pressed.  Stopping Alert");
			stopAlert();
			break;
		case  REASON_COMPLETED:
			//System.out.println("Completed Called.  Stopping Alert");
			NotificationsManager.triggerImmediateEvent(bbhAttribute.NOTIFICATION_MESSAGEPAGE, 0, null, null);
			continueAlert();
			break;
		case REASON_STOP_CALLED:
			//System.out.println("Stop Called.  Do Nothing.");
			// Do nothing
			break;
	}
}

Which basically means, the initial music and vibrate will play and then when they are done and the user hasn’t clicked a button to stop it, the Notification will start before the music and vibrate plays again.

So far, this seems to be the working solution. I hope this is the final solution, because it has been a headache. I hope the BB10 method of notify users has something simple for continuous notification.

DataGrid Row Move

Another question on the Flex Forum about how to move a selected row on the DataGrid up or down, based on a button click.  The answer is straight forward, but with a catch.

When you click on the DataGrid, it will give you an index for the row you have clicked.  The first row in the DataGrid is 0.  If you wanted to simply move a row up and down the DataGrid, then you simply add or subtract 1 from the selected index.


// Move Item Up

SelectedIndex--;

// Move Item Down

SelectedIndex++;

Simple enough.  Unless you sort the DataGrid, which throws that out of wack and is pointless.  So, if you don’t plan on sorting the data, then adding a couple of buttons to move an item up and down within the DataGrid and the ArrayCollection provider, then here is the code.  It also includes checks to make sure you don’t move the item off ofthe grid.

Code for the Move Up Button


protected function moveUpBtn_clickHandler(event:MouseEvent):void
{
var upSelectedIndex:int = myDataGrid.selectedIndex;

// Move Item
if(upSelectedIndex > -1) {
if(upSelectedIndex > 0) {
// Get the selected Object
var moveObject:Object = myArrayCollection.getItemAt(upSelectedIndex);
// Get actual Object location if grid is sorted
var upActualIndex:int = myArrayCollection.getItemIndex(moveObject);
// Remove selected item at actualIndex
myArrayCollection.removeItemAt(upActualIndex);
myArrayCollection.refresh();
// Move both indexes
upActualIndex--;
upSelectedIndex--;
// insert item at actualIndex
myDataGrid.dataProvider.addItemAt(moveObject, upActualIndex);
myArrayCollection.refresh();
// select grid at selectedIndex
myDataGrid.selectedIndex = upSelectedIndex;
} else {
Alert.show("Item is currently at the top of the list.", "Move Alert",  Alert.OK, this);
}
} else {
Alert.show("No Item Selected.", "Move Alert",  Alert.OK, this);
}

}

Code for the Move Down Button


protected function moveDownBtn_clickHandler(event:MouseEvent):void
{
var downSelectedIndex:int = myDataGrid.selectedIndex;

if(downSelectedIndex > -1) {
// Move Item
if(downSelectedIndex < myArrayCollection.length -1) {
// Get the selected Object
var moveObject:Object = myArrayCollection.getItemAt(downSelectedIndex);
// Get actual Object location if grid is sorted
var downActualIndex:int = myArrayCollection.getItemIndex(moveObject);
// Remove selected item at actualIndex
myArrayCollection.removeItemAt(downActualIndex);
myArrayCollection.refresh();
// Move both indexes
downActualIndex++;
downSelectedIndex++;
// insert item at actualIndex
myDataGrid.dataProvider.addItemAt(moveObject, downActualIndex);
myArrayCollection.refresh();
// select grid at selectedIndex
myDataGrid.selectedIndex = downSelectedIndex;
} else {
Alert.show("Item is currently at the bottom of the list.", "Move Alert",  Alert.OK, this);
}
} else {
Alert.show("No Item Selected.", "Move Alert",  Alert.OK, this);
}

}

I did modify the DataGrid, so that the selectioMode is “singleRow”, which simplifies the selectedIndex information. I also set the selectedIndex for the row after the move, so the user can continue to move the row up and down and not have to select it again. Here is the example and source.

UPDATE: Had to set the Down Move to look for the Length of the ArrayCollection -1, because the DataGrid row starts at 0, it is one less than the total length of the ArrayCollection.

View Source

Column Chart with Item Renderer – updated

Okay, it didn’t take me long to get back and do some more fiddling with the Column Chart Item Renderer. I went ahead and changed the static ArrayCollection to a dynamic one, because I was trying to figure out something with Themes and needed a button and the button should do something. The ArrayCollection is fairly simple for this chart, so all I did was create a couple of random values from 0 to 100 and made the “date” value the counter time 10. I set it up so that you can press a button and create new data, which is helpful to see the changes to the columns.

// Create some random data for Yesterday and blank data for today
private function dataCreation():void {
	currentArray.removeAll();

	for(var h:int = 0; h < 4; h++) {
		// Generate Random Data
		var randomClose:Number = Math.floor(Math.random() * 100);
		var randomOpen:Number = Math.floor(Math.random() * 100);
		// create a temporary object and add the three values
		var tmpObject:Object = new Object();
		tmpObject.date = (h + 1) * 10;
		tmpObject.close = randomClose;
		tmpObject.open = randomOpen;

		// insert new tmpObject
		currentArray.addItem(tmpObject);
	}

	// Refresh the data
	currentArray.refresh();
}

After that was fixed, I went ahead to modify the ItemRenderer again, but this time to use a GradientFill and a BitMapFill. I had to reduce what was defined for the rectangle, to just a Graphic item as a holder.

s:Graphic id="graphicHolder" />

And then I declared three different rectangles, one for each type of fill. Again, this was to simplify things and also to make sure I was doing the right thing for each fill. As you can see in the Gradient Fill, I had to set the three GradientEntry items to create the look I need. You could do this all dynamically, but I don’t think most people will try different Gradients for each column. Which is why my Gradient fill only changes the Start, Mid and End color of the Gradient.

<fx:Declarations>
	<s:Rect id="imageRect">
		<s:stroke>
			<s:SolidColorStroke id="rectImageStroke" />
		</s:stroke>
		<s:fill>
			<s:BitmapFill id="imageRectFill" fillMode="repeat" scaleX=".70" scaleY=".70" smooth="true" />
		</s:fill>
	</s:Rect>

	<s:Rect id="gradientRect">
		<s:stroke>
			<s:SolidColorStroke id="rectLinGradStroke" />
		</s:stroke>
		<s:fill>
			<s:LinearGradient id="rectLinGrad">
				<s:entries>
					<s:GradientEntry id="linGradColorStart" ratio="0" alpha=".5" />
					<s:GradientEntry id="linGradColorMid" ratio=".25" alpha=".5" />
					<s:GradientEntry id="linGradColorEnd" ratio=".66" alpha=".5" />
				</s:entries>
			</s:LinearGradient>
		</s:fill>
	</s:Rect>

	<s:Rect id="solidRect">
		<s:stroke>
			<s:SolidColorStroke id="rectSolidStroke" />
		</s:stroke>
		<s:fill>
			<s:SolidColor id="rectSolidColor" />
		</s:fill>
	</s:Rect>	
</fx:Declarations>

From here, it was just setting the color for the Gradients and the appropriate image for the BitMap Fills. Except that it took me a while to figure out the correct syntax for the Image. I couldn’t declare it. I couldn’t just Embed it as the source. I had to Embed it as a Bindable Class and then set the BitmapAsset to that Class and finally make it the source for the BitMapFill.

// Embed the Red Apple Image
[Embed(source="../assets/images/red-apple.png")]
[Bindable]
public var imgRedApple:Class;

// Red Image Fill
var imgRedAppleObj:BitmapAsset = new imgRedApple() as BitmapAsset;
imageRectFill.source = imgRedAppleObj;

I simplified the code so that different columns do only two fills. I had columns doing all three fills, but decided that was too much code and could get really confusing. With the example below;

  • If the 1st column has more than 45, then the Red Apple Image Fill is used, else the Red Gradient Fill
  • If the 2nd column has more than 45, then the Blue Gradient Fill is used, else the Blue Solid Fill
  • If the 3rd column has more than 45, then the Green Apple Image Fill is used, else the Green Solid Fill
  • If the 4th column has more than 45, then the Purple Solid Fill is used, else the Purple Gradient Fill

I created a second file for this example, so that the first example and this example could be viewed separately.
View Source

Column Chart with Item Renderer

When doing the Column Chart with fillFunction for a question in the Flex Forum, the poster said that he was trying to do something a little different. Instead of a simple fill, he wanted the fill to be semi-transparent with a solid border. When I looked at the iFill function, it didn’t have a stroke option and it doesn’t seem that the standard column item deals with the column border. My guess was, that an item renderer should be used for the column instead. Within the item render, you can create a rectangle graphic and then adjust the border and fill parameters. Since this seemed like an interesting exercise, I decided to make myself an example (the best way I learn something).

I started out using the simple array data found in other examples. I will probably update this to something more dynamic later.

[Bindable]
public var SMITH:ArrayCollection = new ArrayCollection([{date:10, close:41.87, open: 30},{date:20, close:45.74, open: 40}, 
{date:30, close:42.77, open: 60}, {date:40, close:48.06, open: 50}]);

And then I attached this to a ColumnChart and create a single ColumnSeries with the “date” on the xAxis and the “close” count on the yAxis. I then added the itemRenderer parameter, which popups a window for creating the component.

<mx:ColumnChart id="myColumnChart1" dataProvider="{SMITH}" >
	<mx:horizontalAxis>
		<mx:CategoryAxis categoryField="date" />
	</mx:horizontalAxis>
	<mx:series>
		<mx:ColumnSeries id="columnSeries1" xField="date" yField="close" itemRenderer="components.myColumnRenderer" />
	</mx:series>
</mx:ColumnChart>

In the ItemRender, the way to kick everything off is to override the set data function. You have to check to see if the value is null first, or you get errors when the chart is initially loading. Once there is data in the value, then you can start altering the renderer for the column.

override public function set data(value:Object):void {
	super.data = value;

	// Check to see if the data property is null.
	if (value== null) {
		return;
	} else {
		// data display and other stuff start here
	}
}

To create the background rectangle, you first have to start with a Graphic and then add the Rect and the parts of the Rect that you want to alter. You could just add the Rect and then alter the stroke and fill dynamically, but having them already defined makes it a little easier. I am only doing a Solid stroke and fill, if you wanted to do gradient or mix gradient and solid, then you definitely would want to do that dynamically. The Graphic is first, since it is on the bottom. Anything you want to display above the rectangle graphic goes afterward in the code.

<s:Graphic>
	<s:Rect id="columnRect">
		<s:stroke>
			<s:SolidColorStroke id="rectSolidColorStroke" />
		</s:stroke>
		<s:fill>
			<s:SolidColor id="rectSolidColor" />
		</s:fill>
	</s:Rect>
</s:Graphic>

Once you are ready to alter the column, you have to create the rectangle graphic for the background and you need to set the width and height to the super call for the column. If you don’t do this, then the rectangle is size 0 and you can’t see anything.

columnRect.width = super.width;
columnRect.height = super.height;

Since you are passing in the column data, you can modify the look of each column either by the xValue or the yValue. In this case, I wanted to change each column based on the yValue. I am only doing a simple solid fill and solid stroke, so I only need to change one color. I declared the colors to make it easier to set the uint values. Finally, I adjust the stroke weight and the fill alpha. These could also be changed based on the column data.

switch(value.xValue) {
	case 10:
		// Red Color
		columnFillColor = colorRed;
		columnStrokeColor = colorRed;
		break;
	case 20:
		// Blue Color
		columnFillColor = colorBlue;
		columnStrokeColor = colorBlue;
		break;
	case 30:
		// Green Color
		columnFillColor = colorGreen;
		columnStrokeColor = colorGreen;
		break;
	case 40:
		// Purple Color
		columnFillColor = colorPruple;
		columnStrokeColor = colorPruple;
		break;
}

rectSolidColorStroke.color = columnFillColor;
rectSolidColorStroke.weight = 2;
rectSolidColor.color = columnStrokeColor;
rectSolidColor.alpha = .3;

This example didn’t take very long to make and I will probably try to add some other feature to this simple chart.
View Source

Column Chart with Fill Function

When you have a column chart, sometimes you want to change the column colors based on the data. A question in the Flex Forum asked how to alter the column color on each column. To do this, yo need to use the fillFunction on a ColumnSeries to set the color of the fill. One issue with doing this, is how it affects the chart legend.

If you use the fills property or the fillFunction to define the fills of chart items, and you want a legend, you must manually create the Legend object for that chart.

But, this just gives me the opportunity to test out two different charting options.

I alter the stacked column chart that I created to answer another question on the forum. Since the column chart in this example is created dynamically, I had to add the fillFunction parameter when creating the column series for the green and red apple columns.

columnSeries1.fillFunction = greenAppleFillFunction;

Then I just created a simple function to change the color of the column if the count was less than 50.

private function greenAppleFillFunction(element:ChartItem, index:Number):IFill {
	var item:ColumnSeriesItem = ColumnSeriesItem(element);
	var count:Number = Number(item.yValue);

	if (count < 50) {
		return scFadeGreen;
	} else {
		return scGreen;,
	}
}

To make things easier, I had already defined my SolidColor values, this makes it easier to keep it consistent throughout the chart. In this example, I am only getting the yValue, but you could also get the xValue, if you wanted to change the column colors based on that.

Next I need to update the legend to reflect the new fillFunctions, which is fairly straight forward and I can also use the defined SolidColor parameters.

<mx:Legend  direction="horizontal" >
	<mx:LegendItem label="Red Apples" fill="{scRed}" />
	<mx:LegendItem label="Red under 50" fill="{scFadeRed}" />
	<mx:LegendItem label="Yellow Apples" fill="{scYellow}" />
	<mx:LegendItem label="Green Apples" fill="{scGreen}" />
	<mx:LegendItem label="Green under 50" fill="{scFadeGreen}" />
</mx:Legend>

I also added a Refresh Data button so that the data can be cycled and the column color changes can be seen.
View Source

Continuos Vibrate and Music Alert until Confirmation

The main phone application that I support at work gets data pushed to it. Part of the data is a Message Page to notify the technician that they have a service call assigned or needing attention. Due to the nature of the service calls, the technician needs this alert to continually go off until they acknowledge the alert. A while back I changed the pop up message to a global message, which makes the pop up show on all screens, regardless if the user is in the application.

public static void displayGlobalMessage(final String gMessage)
{
	UiApplication.getUiApplication().invokeLater( new Runnable() {
			public void run() {
				UiEngine uie = Ui.getUiEngine();
				alertDialogListener  closeListener = new alertDialogListener();
				
				Dialog screen = new Dialog(Dialog.D_OK, gMessage, Dialog.OK,
											Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION),
											Manager.VERTICAL_SCROLL);
				screen.setDialogClosedListener(closeListener);
				uie.pushGlobalScreen(screen, 1, UiEngine.GLOBAL_MODAL);
			}
	});
}

Unfortunately, when I created this option, I some how broke my continuous alert that was previously setup. After many attempts to get it to work again, including using a TimerTask, I finally got the alert to work again.

The first part is creating the AlertListener class that I will use to handled starting the music and vibrate. This was the most important part, because stopping and starting the alert depends on what is to be called. It took a very long time to figure out when to turn off the overall alert (continueAlert) and when to turn on and off the music and vibrate (audioFinished). This is handled in the audioDone function, because the music plays longer than the vibration (which is why nothing is done on vibrateDone and buzzerDone).

public class SDAAlertListener implements AlertListener
{
    private boolean continueAlert;
    private Object continueAlertLock;
    private boolean audioFinished;
    private Object audioFinishedLock;
    
    public SDAAlertListener()
    {
    	//System.out.print("Added Continuos Alert Listener");
        continueAlert = true;
        continueAlertLock = new Object();
        audioFinished = false;
        audioFinishedLock = new Object();
    }
    
    public void startAlert()
    {
    	//System.out.println("Starting Contiuous Alert");
    	SetContinueAlertStatus(true);
    	SetAudioDoneStatus(false); 
    	
    	Alert.startAudio(music.myFogHornTune, 8);
    	Alert.startVibrate(500);
    }
    
    public void continueAlert()
    {
    	//System.out.println("Contiuous Alert to Continue");
    	SetContinueAlertStatus(true);
    	SetAudioDoneStatus(false); 
    	
    	Alert.startAudio(music.myFogHornTune, 8);
    	Alert.startVibrate(500);
    }
    
    public void stopAlert()
    {
    	//System.out.println("Stopping Contiuous Alert");
    	SetAudioDoneStatus(true); 
    	SetContinueAlertStatus(false);
    	
    	Alert.stopAudio();
        Alert.stopBuzzer();
    }
    
    public boolean GetContinueAlertStatus()
    {
        synchronized(continueAlertLock)
        {
            //System.out.println("Getting continueAlertStatus.");
            return continueAlert;
        }
    }
    
    public boolean GetAudioDoneStatus()
    {
        synchronized(audioFinishedLock)
        {
            //System.out.println("Getting buzzerFinished.");
            return audioFinished;
        }
    }
    
    public void SetContinueAlertStatus(boolean value)
    {
    	synchronized(continueAlertLock)
        {
        	//System.out.println("Setting ContinueAlert to " + value);
        	continueAlert = value;
        }
    }
    
    public void SetAudioDoneStatus(boolean value)
    {
        synchronized(audioFinishedLock)
        {
            //System.out.println("Setting audioFinished to " + value);
            audioFinished = value;
        }
    }
    
    public void audioDone(int reason)
    {
    	System.out.println("audioDone function");
    	
    	switch(reason) {
	    	case REASON_KEY_PRESSED:
	    		//System.out.println("Key Pressed.  Stopping Alert");
	    		stopAlert();
	    		break;
	    	case  REASON_COMPLETED:
	    		//System.out.println("Completed Called.  Stopping Alert");
	    		continueAlert();
	    		break;
	    	case REASON_STOP_CALLED:
	    		//System.out.println("Stop Called.  Do Nothing.");
	    		// Do nothing
	    		break;
    	}
    }
    
    public void buzzerDone(int reason)
    {
        // Do nothing.
    }
    
    public void vibrateDone(int reason)
    {
        // Do nothing.
    }
}

The next step is to create an instance of the listener on the main screen, so it is always there. This was one of the issues I was having, because I was adding the listener when the Message Page came in and then removing it after the alert was done. By creating the single listener, I only worry about starting and stopping the music and vibrate.

// Before the main screen is created
public static SDAAlertListener _sdaAlertListener = new SDAAlertListener();
// After the application is created and is only called once
((Application) UiApplication.getUiApplication()).addAlertListener(StartScreen._sdaAlertListener);

Next, I handled the Message Page by sending it to the GlobalMessage display function and creating a loop to check for when the Alert should be played again. The loop checks the Continue Alert Status to see if the button click hasn’t turned it off. Then it checks to see if the audio is done playing. If the Continue Alert is true and the Audio Done is true, then the startAlert function is called again to replay the music and vibration. Otherwise a break stops the loop.

/* Global Dialog display instead of screen pop */
bbhUtil.displayGlobalMessage("New Message Page " + MessagePagingInfo.getCurrentMessage());     
// Start Continuous paging section 
boolean alertStatus = true;
StartScreen._sdaAlertListener.startAlert();
// Start loop 
while (true)
{ 
	alertStatus = StartScreen._sdaAlertListener.GetContinueAlertStatus();
	if(alertStatus)
	{
		if(StartScreen._sdaAlertListener.GetAudioDoneStatus()) {
			//System.out.println("Audio Done Status is True");
			StartScreen._sdaAlertListener.startAlert();
		} else {
			break;
		}
	}
	else
	{ 
		System.out.println("Paging Alert Stop.");
		break;
	}                               
}

Finally, the Global Message display has a close listener attached to it. This listener calls the Alert stop function when the user clicks the “OK” button.

public class alertDialogListener implements DialogClosedListener {
	public void dialogClosed(Dialog dialog, int choice) {
		// Check for OK click and then stop Timer
		//System.out.println("Output Choice: " + choice);
   		 switch (choice) {
   		 	case Dialog.OK : 
   		 		synchronized(UiApplication.getEventLock())
   		 		{
   		 			//System.out.println("Calling Alert Listener Stop");
   		 			StartScreen._sdaAlertListener.stopAlert();
                }
        		break;
            case Dialog.CANCEL : 
            	Status.show("Cancel msg"); 
            	break;
        }
	}
}

This also allows for multiple Message Page alerts, since it turns on and off the same listener. I just wish I hadn’t taken so long in figuring it out.

TextArea auto scroll update

There was a question on the Flex Forum about how to auto-scroll a TextArea. You can set the VerticalScrollPolicy to auto or you can add a little code to valueCommit to move the vertical scroller. Since I lost where I created the example of adding the code to the valueCommit, I decide to remake the example to show the difference. Both text areas are updated every 10 seconds with a random generated sentence, based off of quotes from Mark Twain.

The left TextArea box uses the VerticalScrollPolicy set to “auto”

<s:TextArea id="tmpTxtArea1" width="98%" height="98%" verticalScrollPolicy="auto" />

The right TextArea box uses the valueCommit code to move the verticalScrollBar

<s:TextArea id="tmpTxtArea2" width="98%" height="98%" 
   valueCommit="tmpTxtArea2.scroller.verticalScrollBar.value = tmpTxtArea2.scroller.verticalScrollBar.maximum"  />

View Source

While the right box doesn’t move the TextArea scroll bar 100%, it does move it.

The current issues I need to update with this example is a better random sentence generator and fix the regular expression that is supposed to remove the commas and periods before creating the word array. That’s probably some sort of project I can do later, so the sentences created are a little bit off, since I don’t remember how I did it in the previous example and the site I got the code idea from is no longer available.

Date Sort in a DataGrid – the easy way

While trying to give suggestions to a post on the Flex Forum, I modified my Simple Data Grid to include a column for a Date. The poster had an issue with sorting the column with dates in it. My guess is, the Date value is actually a String object. It really isn’t very hard to create a Date from a string, just parse out the String and feed the appropriate date times into the Date object. Here is how I create the random date value.

// Create a Random Date
var randomMonth:Number = Math.floor(Math.random() * (11));
var randomDay:Number = Math.floor(Math.random() * (27));
var randomHour:Number = Math.floor(Math.random() * (23));
var randomMinute:Number = Math.floor(Math.random() * (59));
var thisDate:Date = new Date();
thisDate.month = randomMonth;
thisDate.date = randomDay;
thisDate.hours = randomHour;
thisDate.minutes = randomMinute;

Since I insert the value into the item Object as a Date object, the sort function knows how to sort for Descending/Ascending. It might take a few more steps to convert a set of String data into objects and insert them into an ArrayCollection, but I think it saves time in the long run, because you work with each value as what they are and not a String that you have to convert to a Number, Int or Date.

Here is the DataGrid example. Not only does sorting by the column header work for the dates, I added a button to sort by the dates. The button switches between Ascending and Descending each time it is clicked.
View Source