Wednesday, August 31, 2011

Listening to Spark ComboBox Prompt Change

The spark ComboBox has a great feature of the prompt being editable by default. So how does one listen to the change of the text in the prompt.

One way of doing this is to listen to the change event fired by the ComboBox

<s:ComboBox id="comboBox" change="changeHandler(event)" />


private function changeHandler(event:IndexChangeEvent)
{
       //do something
}


The other way is to listen to the textChange of the prompt text

private function init():void
{
       comboBox.textInput.addEventListener(TextOperationEvent.CHANGE, textChangeHandler);
}


private function textChangeHandler(event:TextOperationEvent):void
{
       //Do Something
}

There. Hope someone finds use for this.

Monday, August 29, 2011

Full Screen in Flex 4

Flex provides a great way of setting web applications into full screen on the desktop. The following code snippet demonstrates the same.

public function toggleFullScreen(uiComponent:UIComponent):void
{
       if(uiComponent.stage.displayState == StageDisplayState.NORMAL)
              uiComponent.stage.displayState = StageDisplayState.FULL_SCREEN;
       else if (uiComponent.stage.displayState == StageDisplayState.FULL_SCREEN)
              uiComponent.stage.displayState = StageDisplayState.NORMAL;
}

The piece of code will set any component into the full screen mode, except the fact that it does not actually solve the purpose. It actually gets the whole application into full screen mode.
That is because setting the full screen property belongs to the Stage object. The flash runtime however has only one stage and all the components displayed refer to the same stage. [For more refer this]. This brings us to the problem. How do you get only the component to fullscreen. Fortunately, there's a simple way to achieve this. Take a look at the code below and I'll explain the nuances after that.

private var componentWidth:Number;
private var componentHeight:Number;
private var sourceComponent:UIComponent;


public function toggleComponentFullScreen(uiComponent:UIComponent):void
{
var stage:Stage = FlexGlobals.topLevelApplication.stage;

if(uiComponent != null && stage.displayState == StageDisplayState.NORMAL)
{
this.componentWidth = uiComponent.width;
this.componentHeight = uiComponent.height;
this.sourceComponent = uiComponent;

this.sourceComponent.width = stage.stageWidth;
this.sourceComponent.height = stage.stageHeight;

var p:Point = new Point(0,0);
p=view.localToGlobal(p);

var sourceRect:Rectangle = new Rectangle(p.x, p.y, stage.stageWidth, stage.stageHeight);

this.fullScreenHandler(null, sourceRect);
}
}


private function fullScreenHandler(event:FullScreenEvent = null, sourceRect:Rectangle = null):void
{
var stage:Stage = FlexGlobals.topLevelApplication.stage;

if(event == null || !event.fullScreen)
{
if((stage.displayState == StageDisplayState.NORMAL) && event == null)
{
if(!stage.hasEventListener(FullScreenEvent.FULL_SCREEN)
stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullScreenHandler);

stage.displayState = StageDisplayState.FULL_SCREEN;
stage.fullScreenSourceRect = sourceRect;
}
else
{
stage.displayState = StageDisplayState.NORMAL;
stage.fullScreenSourceRect = null;

this.sourceComponent.width = this.componentWidth;
this.sourceComponent.height = this.componentHeight;
}
}


toggleComponentFullScreen
So what we do here is that we save the component's width and height in separate variables to be used later. When the component's state needs to be changed to FULL SCREEN, we set the components width and height to fill the stage. Next we convert the components local coordinates to global coordinates and create a Rectangle with them that can mask the screen.

fullScreenHandler
The handler is a generic function that can also be called from outside without the event. This function checks the displayState of the stage and based on that either masks it with the component's mask rectangle or removes the mask and sets the components width and height back to the original ones.

I found this useful in my application. Hope this can help someone. Cheers.


UIComponent.stage

An important class of note in Flex applications is the Stage. Every DisplayObject has a reference to the stage class and it is worth noting that the Flash runtime has only one Stage object. What the stage does is it acts as a container for all the display objects. Any display objects that we can see on the screen will have a reference to the stage of the run time and all of them will refer to the same object.
It is also important to note when the stage for a particular object is set. Before initialization the stage is null for every object. During the initialization of a component Flex fires an event called "ADDED_TO_STAGE". This is when the object is added to the main stage. If one needs to use the stage for anything, it is advisable to listen to this event so as to avoid null pointer exceptions.
A simple usage of the stage is to set the application to fill the screen,
       uiComponent.stage.displayState = StageDisplayState.FULL_SCREEN;
Other usage of the stage may be for resize operations or to fire a common event for all the Objects on display.

Friday, August 26, 2011

Free Export Button

Something I tried....
ExportButton provides you the ability to

1. Export to PDF
2. Export to JPG
3. Export to PNG
4. Send for print
5. Do a fullscreen

APIs:

exportType - This is the type of export. Choose from ExportUtilButton.TYPE_PDF, ExportUtilButton.TYPE_JPG, ExportUtilButton.TYPE_PNG, ExportUtilButton.TYPE_PRINT, ExportUtilButton.TYPE_FULLSCREEN

fileName - The name of the file to be saved. The extensions will be added by the program.

childComponent - The UIComponent to be exported.

onCancel - Function to be called on cancellation of file download

onComplete - Function to be called on completion of the file download.

icon - The icon that you want to be set on the button.

Sample Application Usage here
I'll soon try and put proper sample in some time. :)
You can download the swc here
Do feel free to add comments, reviews or any changes that you may require.

-Cheers

Adding multiple images with AlivePDF

Furthur to my work with AlivePDF, I had to add multiple images into the PDF that I was creating. This seemed straight forward at first. So I did it this way,


private function exportToPDF(childrenComponents:Array):void
{
var pdfEncoder:PDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.A4);
pdfEncoder.setDisplayMode(Display.REAL);

for (var i:uint = 0; i < childrenComponents.length; i++)
{
        pdfEncoder.addPage();
pdfEncoder.addImage(dObj, new Resize(Mode.FIT_TO_PAGE, Position.LEFT);
}
var bytes:ByteArray = pdfEncoder.save(Method.LOCAL);
_fileReference.save(bytes, (this.fileName + ".pdf"));
}

That worked fine but now I needed to bring them into the same page and add new pages dynamically. So here it went,

EDIT: Changed the code below to set margins and give out better resolution in the PDF.

private function exportToPDF(childrenComponents:Array):void
{
var pdfEncoder:PDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.A4);
pdfEncoder.setDisplayMode(Display.REAL);
pdfEncoder.setMargins(10, 10, 5, 20);

var pageNum:int = 0;
pdfEncoder.addPage();
pageNum++;
var imgY:int = 0;

for (var i:uint = 0; i < this.childrenComponents.length; i++)
{
var pageWidth:Number = pdfEncoder.getCurrentPage().w - 15;
var pageHeight:Number = pdfEncoder.getCurrentPage().h - 30;

var dObj:DisplayObject = this.childrenComponents[i] as DisplayObject;
var imgScaleFactor:Number = dObj.width/dObj.height;


var pw:int = 0;
var ph:int = 0;

if (pageWidth > dObj.width)
{
uiComp = (dObj as UIComponent);
pw = dObj.width;
if(pageHeight > dObj.height)
ph = dObj.height;
else
ph = pw/imgScaleFactor;
}
else
{
pw = pageWidth;
ph = pw/imgScaleFactor;
}

if ((imgY + ph) > pdfEncoder.getCurrentPage().h)
{
pdfEncoder.addPage();
pageNum++;
imgY = 0;
}

pdfEncoder.addImage(dObj, new Resize(Mode.NONE, Position.LEFT), 0, imgY, pw, ph);
imgY = imgY + ph + 10;
}
var bytes:ByteArray = pdfEncoder.save(Method.LOCAL);
_fileReference.save(bytes, (fileName + ".pdf"));
}



And it worked. All the "pw", "ph", "imgScaleFactor" have all been calculated so that I can get proper resolution on the PDF file. If you can better it, be sure to post it here.. :)
If the size exceeds that of the page, it should create a new Page and add it there. I haven't tested this properly, so it might be a bit buggy.
Cheers.


Printing image in Flex

This is a very simple excercise to do in Flex. The following code snippet demonstrates it.


private function printImage(component:UIComponent):void
{
if (component)
{
var printJob:FlexPrintJob = new FlexPrintJob();
if (printJob.start())
{
printJob.addObject(component, FlexPrintJobScaleType.FILL_PAGE);
printJob.send();
}
}
}


Explanation:

The code essentially takes the reference of the component which is to be printed. FlexPrintJob starts a new job.
printJob.start() checks whether the system is connected to a printer and that the printer is ready
FlexPrintJobScaleType sets the type of scaling that is required. NONE does not scale anything.

Thursday, August 25, 2011

Using AlivePDF to export PDF in Flex

I have recently been working with AlivePDF for exporting UIComponents into PDF and liked it very much. It is a simple tool, easy to use. A small code snippet

private function exportToPDF(uiComponent:IUIComponent):void

{
_fileReference.addEventListener(Event.COMPLETE, 
function callOnComplete(event:Event):void
{
if(onComplete != null) 
onComplete.call(null, event);
_fileReference.removeEventListener(Event.COMPLETE, callOnComplete);
})
_fileReference.addEventListener(Event.CANCEL, 
function callOnCancel(event:Event):void
{
if(onCancel != null) 
onCancel.call(null, event);
_fileReference.removeEventListener(Event.CANCEL, callOnCancel);
})

var pdfEncoder:PDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.A4);
pdfEncoder.setDisplayMode(Display.REAL);
pdfEncoder.addPage();
pdfEncoder.addImage(uiComponent as DisplayObject, 
                        new Resize("FitToPage", Position.LEFT));


var bytes:ByteArray = pdfEncoder.save(Method.LOCAL);
_fileReference.save(bytes, (fileName + ".pdf"));
}


I am assuming that we are using Flash 10. For player below that version, FileReference would not work. You'd need to do a server side implementation to download the PDF.

If anyone has any questions, please feel free to ask.


Monday, August 8, 2011

Binding itemRenderer with property not in data object

I really had a tough time with this one. Almost all the approaches I took with this failed.

So, what I really wanted to do was to add a line in the itemRenderer based on whether some property outside matched with the itemIndex or not. Getting the data in was not a problem. Binding it sure was.

Using outerDocument.property_name works only if the line was predefined, but since predefining Graphics elements caused a huge performance issue. I could not use that approach. Instead I added the code to draw the line in the updateDisplayList function.

Now the only thing was to bind the data. I tried to use outerDocument from the child and failed. Even the ClassFactory for the itemRenderer failed. So, then I tried adding event listeners on the change event of the NumericStepper in the parent and changed my display accordingly. Worked like a charm.


ItemRenderer Code:

private function creationComplete_eventHandler(event:Event):void
{
      outerDocument.nStepper.addEventListener(Event.CHANGE, this.onStepperChange);
}

private function onStepperChange(event:Event):void
{

       //Your code goes here.
}


I am still at a loss though on why the others failed. If anyone has any ideas, do share.