Monthly Archives: June, 2007

Bug: SWFLoader doesn’t load in TabNav / ViewStack

This is bug report SDK-11280 where having a SWFLoader in multiple views of a TabNavigator or ViewStack doesn’t work correctly. Take the following code:

<mx:ViewStackwidth=”100%” height=”100%” creationPolicy=”all”>
<mx:Canvas id=”view1″ label=”View 1″>
<mx:SWFLoader id=”image1″ source=”img1.swf” width=”100%” height=”100%”/>
</mx:Canvas>
<mx:Canvas id=”view2″ label=”View 2″>
<mx:SWFLoader id=”images2″ source=”img2.swf” width=”100%” height=”100%”/>
</mx:Canvas>
</mx:ViewStack>

In this example, the first loader will show up fine. When you switch to the second view, you don’t see anything.

Solution: Add a minWidth and minHeight to your SWFLoader. This will solve your problem – <mx:SWFLoader id=”images2″ source=”img2.swf” width=”100%” height=”100%” minWidth=”100″ minHeight=”100″ />

How to know when my itemRenderer is selected

This problem was logged as bug SDK-9846. It didn’t seem that easy for an itemRenderer to know that it was selected so that it can display itself accordingly. However, it is easy for an itemRenderer to know that it’s selected. As mentioned by Alex Harui, in the bug report, here is the simple line that let’s you know:

ListBase(owner).isItemSelected(data)

If you want to change the appearance of your itemRenderer depending on whether it is selected, you should do so using the line above within the updateDisplayList function that you have overridden.

In the following example, the text in the “price” column changes some of its attributes when selected.

Demo: selectExample.swf

Sample Code: selectExample.mxml, MyLabel.as

Bug: Tree Scrollbar Doesn’t Show Up

A customer pointed out to us that in some cases when a Tree’s scrollPolicy is set to ‘auto’, the scrollbar doesn’t show up when you add data and it doesn’t disappear when you remove data. This bug only shows up in Flex 2.0.1 with our latest released hotFix. The bug is not in Flex 2.0.1 and it is NOT in the Moxie Public Beta. The example below is the original Application which has a delay (created by a Timer) before adding a bunch of nodes to the Tree. When the nodes are added and a scrollbar is needed, it does not show up.

Original Bug Application : treeScrollAuto.mxml, treeScrollAuto.swf

After spending some time looking at this Deepa Subramaniam came up with the following solution to this problem using a custom dataDescriptor.

Fixed Application : treeScrollAutoFixed.mxml, myDescriptor.as, treeScrollAutoFixed.swf

Bug: Problems with Accordion and the ‘show’ event

This bug (SDK-11344) was brought up internally and investigated by my co-worker Kishan Venkataramana. The views in an Accordion don’t trigger the “show” event the first time they load. The workaround is to set creationPolicy=”all” on the Accordion. Now the “show” event will trigger the first time you load an Accordion view for all views except the first one. If you need some functionality triggered when the Accordion first loads with its first view, I would just call a function on the creationComplete handler as well as the show event. Here is a simple example:

<mx:Accordion width="300" height="250" creationPolicy="all">
    <mx:Canvas show="someFunc(..)" creationComplete="someFunc(..)">
        <mx:Label text="one"/>
    </mx:Canvas>
    <mx:Canvas show="someFunc(..)">
        <mx:Label text="two"/>
    </mx:Canvas>
</mx:Accordion>

Changing text color in a DataGrid using itemRenderers

People constantly ask us how to change cell attributes like text color of individual DataGrid cells depending on data. I just answered a similar question on flexcoders today. If you are making any changes to individual cells, you will almost always use a custom itemRenderers. And… if you are changing the cell attributes depending on data, you will often need to override the set data function in your itemRenderer. Here is a sample of overriding set data to change the text color of a cell’s text if the data in the cell is greater than 10:

override public function set data(value:Object):void
{
if(value != null)
{
super.data = value;
if(value[DataGridListData(listData).dataField] < 10) {
setStyle(“color”, 0xFF0000);
}
else {
setStyle(“color”, 0x000000);
}
}
}

Demo: itemSample.swf

Sample Code:itemSample.mxml, CustomComp.as

If you want to make appearance of the cell to depend on a particular column’s data, then, you can change the code: value[DataGridListData(listData).dataField] to just use something like value.quantity (assuming “quantity” is the column dataField you are interested in.

Dragging from a Tree to a List/TileList

In the Flex framework, we have made dragging and dropping within various List components fairly trivial. Assuming that your data is similar, you simply need to add dragEnabled=true to your source and then dropEnabled=false for your destination. However, the one exception in this case is Tree. By default, you cannot drag any items from any other drag enabled List component (other than another tree). If you look at the source of the framework, you will see that all of the event handlers used for TileList, List, HorizontalList and DataGrid are in ListBase.as. However, Tree has its own custom drag event handlers. Therefore, if you want to share data between another List component and a Tree using drag and drop, you will need to override all of the drag event handlers. These handlers include dragEnter, dragDrop, dragComplete and dragOver. Here is an example Application where you can drag items from a Tree to a TileList. The items will be removed from the Tree.

Here is the code for the Tree and TileList:

<mx:TileList id=”srcTileList” dropEnabled=”true” dragOver=”doDragOver(event)”
dragEnter=”doDragEnter(event)”dragDrop=”doDragDrop(event)” columnWidth=”100″ /><mx:Tree id=”destTree” dragEnabled=”true” labelField=”@label” showRoot=”false” dragComplete=”doDragComplete(event)” width=”250″ />

And here are my event handlers for each drag events:

public function doDragOver(event:DragEvent) : void
{
event.preventDefault();DragManager.showFeedback(event.ctrlKey ? DragManager.COPY : DragManager.MOVE);
TileList(event.target).showDropFeedback(event);
}public function doDragEnter(event:DragEvent): void
{
event.preventDefault();DragManager.acceptDragDrop(TileList(event.target));
DragManager.showFeedback(event.ctrlKey ? DragManager.COPY : DragManager.MOVE);
TileList(event.target).showDropFeedback(event);
}public function doDragDrop(event:DragEvent): void
{
event.preventDefault();
var myTileList:TileList = TileList(event.target);
myTileList.hideDropFeedback(event);if (event.dragSource.hasFormat(“treeItems”))
{
if (!myTileList.dataProvider)
// Create an empty collection to drop items into.
myTileList.dataProvider = [];

var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
for (var i:int = items.length – 1; i >= 0; i–)
{
myTileList.dataProvider.addItemAt(String(items[i].@label), TileList(event.target).calculateDropIndex(event));
}
}
}

public function doDragComplete(event:DragEvent): void
{
event.preventDefault();
if (event.action == DragManager.MOVE && Tree(event.target).dragMoveEnabled)
{
var target:Tree = Tree(event.target)
if (event.relatedObject != this)
{
//if we dropped on another component
//then we need to remove from ourself first
var items:Array = event.dragSource.dataForFormat(“treeItems”) as Array;
var parent:*;
var index:int;

//do the remove
for (var i:int=0; i<items.length; i++)
{
parent = target.getParentItem(items[i]);
index = getChildIndexInParent(parent, items[i], target);
target.mx_internal::removeChildItem(parent, items[i], index);
}
}
}
}

private function getChildIndexInParent(parent:Object, child:Object, target:Tree):int
{
var index:int = 0;
if (!parent)
{
var cursor:IViewCursor = ICollectionView(target.dataProvider).createCursor();
while (!cursor.afterLast)
{
if (child === cursor.current)
break;
index++;
cursor.moveNext();
}
}
else
{
if (parent != null && target.dataDescriptor.isBranch(parent) &&
target.dataDescriptor.hasChildren(parent))
{
var children:ICollectionView = target.dataDescriptor.getChildren(parent);
if (children.contains(child))
{
for (; index < children.length; index++)
{
if (child === children[index])
break;
}
}
}
}
return index;
}

Demo: TileList2Tree.swf
Source code: TileList2Tree.mxml

Flex 3 and Backwards Compatibility … bugs and how to

After many months of work, we officially released our first public beta of “Moxie” or Flex 3. For myself, on the Flex Framework QA team, one of the biggest things about this the fact that we really are on our way to being open source for the framework. No more secrets… our builds are coming out nightly. You can get them here. And, you can log as many bugs as your heart desires at our Public Bug Base. Really, you can search and see all of our bugs from WAY back. There are definitely bugs older than my life on the team (which is 2004).

Anyways, the thing I wanted to talk about today was some of the backwards compatibility issues you might come upon when you try to upgrade your Application to Flex 3. All of the Compatibility Issues should be written up in the Release Notes, but, I just wanted to highlight a couple of them. First and foremost, you may see some different behaviors with your Button. Specifically, you may notice some Labels that may be truncated. This is NOT a bug. I repeat… its not a bug. In Flex 3, there was a major bug fix to get padding working for a Button. This includes the styles paddingLeft, paddingRight, paddingTop and paddingBottom. Previously, there was a hack in the code and these didn’t really work. Our Button measurement logic had a default of about 10 pixels on its left and right side which was adjusted depending on your label length and whether you had an icon. But, setting padding styles didn’t really work. Now, padding works and the default paddingRight and paddingLeft are 10. Therefore, if you have a button like:

<mx:Button width=”60″ label=”Submit”/>

This may render fine in Flex 2.0.1. In Flex 3, the Submit label is truncated. To fix your problem, you can just reset the paddingLeft and paddingRight to smaller values.

<mx:Button width=”60″ label=”Submit” paddingRight=”5″ paddingLeft=”5″/>

If you do not want to go through the trouble of updating your code, Flex 3 comes with a way for you to go back to the old behavior of Flex 2.0.1. If you compile your application with the additional arguments “-compatibility-version=2.0.1”. You won’t getting the Button padding fix and your Application should look exactly the same as before. However, if you do use this compiler argument, there are several other Flex 3 behavior changes that you won’t get. They are all described here. If you want, you can search the Flex SDK code for the inclusion of “FlexVersion”. These are the areas of the code where we have provided various code paths for using old Flex 2.0.1 behavior or the new Flex 3 behavior.

On caveat for using the -compatibility-version compiler argument is that it doesn’t work right now if your Application is using a DataGrid. We found this late in our cycle and therefore didn’t fix it in this beta. So, you will get a runtime error if you use the compatibility flag with a DataGrid (“Bug SDK-11231“).

Generic Background Gradient for Containers

A customer submitted a bug this week requesting an easy way to use background gradients in our containers like VBox, HBox, etc. This sounded like a great request and I was surprised that we didn’t already offer this in the framework. I logged the bug as bug SDK-111200 and sought the help of our developer Glenn who is the master of all things styles, skins and themes. Glenn offered the simple solution of using a style we already have called applicationControlBar. Here is an example where you can control the hightlights, colors and alphas of the gradient:

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; backgroundColor=”white”>
<mx:Style>
.gradientBackground { borderStyle: applicationControlBar; fillColors: #EEEEFF, #8888AA; fillAlphas: 0.7, 0.7; highlightAlphas: 0, 0; }
</mx:Style>
<mx:VBox styleName=”gradientBackground” width=”300″ height=”300″ /> </mx:Application>

This worked great. However, the customer came back to tell me that this style shifted all of the components in the container by one pixel. Apparently, this is because the applicationControlBar style has a default 1 pixel border. Oh well. So, if someone wants a simple gradient, and doesn’t care about that one pixel border, use the code above.

However, Glenn came up with an even better solution. I have to repeat, all of these solutions were kindly provided by Glenn Ruehle. I was just the messenger in this transaction with the customer. Glenn went ahead and created a custom gradient class that you can use on any container and control the gradientType, fillColors, fillAlphas, angle, and focalPointRatio of the style. Here is what it looks like:

gradient

Here is the code for this simple Application:

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; backgroundColor=”white” width=”200″ height=”170″ >
<mx:Style>
.gradientBackground1 {
backgroundImage: ClassReference(“GradientBackground”);
backgroundSize: “100%”;
fillColors: #EEEEEE, #999999;
fillAlphas: 0.5, 0.5;
}

.gradientBackground2 {
backgroundImage: ClassReference(“GradientBackground”);
backgroundSize: “100%”;
gradientType: radial;
fillColors: #FFCC33, #999999;
fillAlphas: 0.2, 0.5;
angle: 210;
focalPointRatio: 0.75;
}
</mx:Style>

<mx:VBox styleName=”gradientBackground1″ width=”100″ height=”50″ />
<mx:VBox styleName=”gradientBackground2″ width=”100″ height=”50″ />
</mx:Application>

And, here is Glenn’s code for the gradient background style:

package
{
import mx.skins.ProgrammaticSkin;
import flash.geom.Matrix;

public class GradientBackground extends ProgrammaticSkin
{
override public function get measuredWidth():Number
{
return 20;
}

override public function get measuredHeight():Number
{
return 20;
}

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
var fillColors:Array = getStyle(“fillColors”);
var fillAlphas:Array = getStyle(“fillAlphas”);
var gradientType:String = getStyle(“gradientType”);
var angle:Number = getStyle(“angle”);
var focalPointRatio:Number = getStyle(“focalPointRatio”);

// Default values, if styles aren’t defined
if (fillColors == null)
fillColors = [0xEEEEEE, 0x999999];

if (fillAlphas == null)
fillAlphas = [1, 1];

if (gradientType == “” || gradientType == null)
gradientType = “linear”;

if (isNaN(angle))
angle = 90;

if (isNaN(focalPointRatio))
focalPointRatio = 0.5;

var matrix:Matrix = new Matrix();
matrix.createGradientBox(unscaledWidth, unscaledHeight, angle * Math.PI / 180);

graphics.beginGradientFill(gradientType, fillColors, fillAlphas, [0, 255] , matrix, “pad”, “rgb”, focalPointRatio);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.endFill();
}
}
}

Hopefully, this same example will show up in the Flex Cookbook where Glenn might post it himself.

Introduction

Just a bit of background… I’ve been working on the quality of the Adobe Flex Framework since 2004 and I’ve seen so many bugs pass across my desk. Still, many of the same questions get asked on flexcoders, blogs etc. as to how to get around them. Hopefully, I’ll shed light on some of them. Since the Flex Framework bug base will soon be open to the public, I’ll reference bugs by numbers and you can look at the details of the bugs themselves. Thanks, and may you find the bugs you are looking for along with some beautiful butterflies.