Flex 3: inquiry about printing - actionscript-3

I have created a scheduling application that spans upwards of 16 weeks. I would like to be able to print the schedule using Flex. I've created a test application that lists incrementing dates. These dates obviously stretch longer than the width of my computer. I would like for my print function to print the entire width of dates across several pages... currently, it prints just what appears on my screen. Is there a way to accomplish this?
Below is the app i've created. There are some calls to custom functions, but they in no way relate to the issue at hand:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init();">
<mx:Script source="functions/dateFunctions.as" />
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.collections.ArrayCollection;
[Bindable] public var date:Date = new Date;
public var numDays:Number = 50;
[Bindable] public var datesAC:ArrayCollection = new ArrayCollection;
public function init():void
{
var tempDate:String = new String;
for (var i:int = 0; i < numDays; i++)
{
tempDate = dateToNumText(rightDate(date.getMonth(), date.getDate() + i, date.getFullYear()));
datesAC.addItem(tempDate);
}
}
private function printMe() :void
{
var pj:PrintJob = new PrintJob();
pj.start();
// setTimeout(function() :void { finishPrinting(pj);}, 1);
finishPrinting(pj);
}
private function finishPrinting(pj:PrintJob): void {
pj.addPage(this);
pj.send();
}
]]>
</mx:Script>
<mx:VBox width="100%" height="100%">
<mx:Button id="print" label="Start Print" click="printMe()" />
<mx:HorizontalList id="dateList" dataProvider="{datesAC}" width="100%" height="100%" useRollOver="false">
<mx:itemRenderer>
<mx:Component>
<mx:Canvas borderColor="#000000" borderSides="right" borderStyle="solid">
<mx:Text text="{data}" textAlign="center" color="#000000" width="100" />
</mx:Canvas>
</mx:Component>
</mx:itemRenderer>
</mx:HorizontalList>
</mx:VBox>
</mx:Application>

You would have to break up/paginate content on your own. You can send each such logically broken up page to PrintJob.appPage() API so it becomes a printed paper. At present, what is happening would be that content would be getting clipped.

I guess you should use PrintJob.addPage() to add your dates to print job.

Related

LinkButton rendered inside a grid shows a horizontal scroll bar when length of text is big

I am trying to create links inside data grid cells and I am unable to remove the horizontal scroll bar
Here is my example
///////////////////////////////////////////
//GridExample.mxml - Main application
///////////////////////////////////////////
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<mx:Box id="myCustomBox" height="100%" width="100%" initialize="assignData();">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.collections.HierarchicalData;
[Embed("dialog_error.png")]
private var myImage:Class;
public function assignData():void {
var retVal:ArrayCollection = new ArrayCollection();
var hData:HierarchicalData = new HierarchicalData();
var s:String = '[{"Property":"AA","Value":2,"RowIdentifier":"AA"},{"Property":"BB","Value":"Nice looking link","RowIdentifier":"BB"},{"Property":"CC","Value":"sdfgusgdfugadgfuasygdfgauidsguiasgdfugasuidfguiasg","RowIdentifier":"CC"}]';
var pSets:Object = JSON.parse(s);
var propertySets:Array = pSets as Array;
for (var i:int=0;i<propertySets.length;i++)
{
var item:Object = propertySets[i];
var arrayElt:Object = {Property: item["Property"], RowIdentifier: item["RowIdentifier"], Value: item["Value"]};
retVal.addItem(arrayElt);
}
hData.source = retVal;
myGridId.dataProvider = hData;
}
]]>
</fx:Script>
<mx:AdvancedDataGrid id="myGridId"
variableRowHeight="true"
width="100%"
showHeaders="false"
includeInLayout="{myGridId.visible}"
defaultLeafIcon="{null}"
>
<mx:columns >
<mx:AdvancedDataGridColumn dataField="Property" headerText="Property" backgroundColor="#E5EFF5" width="0.4" wordWrap="true" />
<mx:AdvancedDataGridColumn dataField="Value" headerText="Value" backgroundColor="white" width="0.6" itemRenderer="ExampleRenderer"/>
<mx:AdvancedDataGridColumn dataField="RowIdentifier" visible="false"/>
</mx:columns>
</mx:AdvancedDataGrid>
</mx:Box>
</s:WindowedApplication>
///////////////////////////////////////////
//ExampleRenderer.mxml - ItemRenderer used
///////////////////////////////////////////
<?xml version="1.0"?>
<mx:Box xmlns:mx="http://www.adobe.com/2006/mxml" textAlign="center" creationComplete="renderNow()" >
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;
import mx.controls.LinkButton;
import mx.controls.Alert;
private function renderNow():void {
var dataObject:Object = super.data;
var rowId:String = dataObject["RowIdentifier"];
var label:LinkButton = new LinkButton();
label.addEventListener(MouseEvent.CLICK, mouseClick);
label.percentWidth = 100;
label.label = dataObject["Value"];
label.setStyle("horizontalScrollPolicy","off");
label.setStyle("textAlign","left");
label.setStyle("paddingLeft",3);
this.addChild(label);
}
override public function set data(value:Object):void
{
super.data = value;
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
private function mouseClick(e:MouseEvent):void{
Alert.show("Clicked on a link");
}
]]>
</mx:Script>
</mx:Box>
The LinkButton produced nice links that can be clicked and in a web view it looks really good with a nice hover producing an underline (just like a link) etc but when the text is long, the horizontal scroll bar obscures the text itself. How do I get rid of the horizontal scrollbar (I have tried turning horizontalScrollPolicy off and that does not help). I do not mind if the extra text over and beyond the row length is hidden as I am going to add a tooltip anyway to every cell. Any help here is appreciated.
Thank you.
Setting the minWidth to 0 along with the percentWidth=100 fixed it.

How to get all the values of one column and calculate a total

I am making a program where people can create their own food schedule.
I also want to calculate the total amount of calories per food schedule.
So first off people can add items to a list which are saved in an arraycollection:
<s:List includeIn="State2" x="12" y="533" width="428" height="157" dataProvider="{acKoop}"
enabled="true" change="totalcal(event)">
<s:itemRenderer>
<fx:Component>
<mx:Label text="{data.Type} {data.Kcal}" />
</fx:Component>
</s:itemRenderer>
</s:List>
I want a function that retrieves all the values of data.Kcal and then makes a Sum of it.
public function totalcal(event:Event):void{
var price:Number=acKoop[event.columnIndex].Kcal;
total += price;
}
Here the code of link I was send, maybe will be useful:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
[Bindable]
private var acKoop:ArrayCollection = null;
public function init():void{
var arr:ArrayCollection = new ArrayCollection();
var obj:Object = new Object();
obj.Type = "TYPE1";
obj.Kcal = 10;
arr.addItem(obj);
obj = new Object();
obj.Type = "TYPE2";
obj.Kcal = 50;
arr.addItem(obj);
acKoop = arr;
}
public function totalcal(event:Event):void{
var i:Number = 0;
for each(var obj:Object in ArrayCollection(List(event.currentTarget).dataProvider)){
i = i + obj.Kcal;
}
Alert.show("Total of Kcal = " + i.toString());
}
]]>
</fx:Script>
<s:List dataProvider="{acKoop}"
enabled="true" change="totalcal(event)">
<s:itemRenderer>
<fx:Component>
<mx:Label text="{data.Type} {data.Kcal}" />
</fx:Component>
</s:itemRenderer>
</s:List>
</s:Application>
Basically, on the change event I take the dataprovider information from the event, and calculate the TOTAL with the for each loop. I hope this will be useful.

Flex: Why are TileList images disappearing on drag?

I'm developing a Flex Air (desktop) application that loads images from the local filesystem into a TileList. The user will then be able to drag (copy) these images out of the list onto another control.
I've finally got the images showing up correctly (and not disappearing after scrolling the TileList) but they seem to disappear from the TileList at the start of a drag operation.
I come from a .NET background and am just learning AS3/Flex, so if you see me using any anti-patterns here, feel free to point them out!
Sample code follows (I've tried to make this as minimal as possible).
Test.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
[Bindable]
protected var _pics:ArrayCollection = new ArrayCollection();
protected function picsList_creationCompleteHandler(event:FlexEvent):void
{
const imageFolderPath:String = "c:\\users\\bbrooks\\Pictures";
var imageDir:File = new File(imageFolderPath);
var imageFiles:Array = imageDir.getDirectoryListing();
for each(var imageFile:File in imageFiles)
{
_pics.addItem(new PictureObject(imageFile.nativePath));
}
// give images a chance to load
var timer:Timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, onTimerExpired);
timer.start();
}
protected function onTimerExpired(event:TimerEvent):void
{
picsList.dataProvider = _pics;
}
]]>
</fx:Script>
<mx:TileList id="picsList" x="0" y="0" width="100%" height="100%" dragEnabled="true" dragMoveEnabled="false"
creationComplete="picsList_creationCompleteHandler(event)" >
<mx:itemRenderer>
<fx:Component>
<mx:Image width="75" height="75" source="{data.image}" />
</fx:Component>
</mx:itemRenderer>
</mx:TileList>
</s:WindowedApplication>
PictureObject.as:
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.net.URLRequest;
import mx.controls.Image;
[Bindable]
[RemoteClass]
public class PictureObject extends Object
{
protected var _image:Image = null;
public function get image():Image { return _image; }
public function set image(newValue:Image):void { _image = newValue; }
public function PictureObject(path:String)
{
var imageLoader:Loader = new Loader();
imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
imageLoader.load(new URLRequest(path));
}
protected function onImageLoaded(e:Event):void
{
var imageLoader:Loader = LoaderInfo(e.target).loader;
var bmp:Bitmap = Bitmap(imageLoader.content);
_image = new Image();
_image.smoothBitmapContent = true;
_image.source = new Bitmap(bmp.bitmapData);
_image.width = imageLoader.width;
_image.height = imageLoader.height;
}
}
}
I will mostly reply to your secondary question (guessing it will resolve the primary in one fell swoop): as anti-patterns go, this is not a bad example ;)
You are manually loading the images while the Image class has the loading built-in; just give a URL to its source property and it will automatically load it.
You are setting an Image instance as the source of another Image instance; the source property expects URLs or ByteArrays; I'm surprised it doesn't throw an error; the Image class is probably smart enough to extract the source of the other Image instance.
The Timer is redundant. As said, an Image automatically takes care of loading its content.
The s:Image tag isn't wrapped in an ItemRenderer and hence should have no access to a data property: this code shouldn't even compile.
There's no point in having a Bindable property _pics if you don't plan on binding it.
You use the mx TileList. Why not use the more "modern" Spark version? (This doesn't mean the mx class won't work in a Spark application though).
So you have a lot of deleting to do. You can scrap the PictureObject class altogether; remove the Timer code; and just add the URL Strings to the _pics collection. As a plus you could also replace the mx TileList with a Spark List with TileLayout; something like this:
<s:List id="picsList">
<s:layout>
<s:TileLayout />
</s:layout>
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer>
<s:Image source="{data}" />
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
The ActionScript part could be reduced to this:
const imageFolderPath:String = "c:\\users\\bbrooks\\Pictures";
var imageDir:File = new File(imageFolderPath);
var imageFiles:Array = imageDir.getDirectoryListing();
picsList.dataProvider = new ArrayCollection(imageFiles);
Thanks RIAstar. Your answer put me on the track to solving the problem. The new sample code appears below.
The original problem seems to have been with the mx:Image control. Not sure why, but using the s:Image control seems to work.
Granted, this could be accomplished without the PictureObject class at all by simply setting the data source to a list of file paths, but I'm using the image data later and I wanted to get it working by supplying the image data to the custom renderer dynamically.
Test.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.components.Image;
protected var _pics:ArrayCollection = new ArrayCollection();
protected function picsList_creationCompleteHandler(event:FlexEvent):void
{
const imageFolderPath:String = "c:\\users\\bbbrooks\\Pictures";
var imageDir:File = new File(imageFolderPath);
var imageFiles:Array = imageDir.getDirectoryListing();
for each(var imageFile:File in imageFiles)
{
if (imageFile.extension == "jpg")
{
_pics.addItem(new PictureObject(imageFile.nativePath));
}
}
// give images a chance to load
var timer:Timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, onTimerExpired);
timer.start();
}
protected function onTimerExpired(event:TimerEvent):void
{
picsList.dataProvider = _pics;
}
]]>
</fx:Script>
<s:List id="picsList" x="0" y="0" width="100%" height="100%"
creationComplete="picsList_creationCompleteHandler(event)"
dragEnabled="true" dragMoveEnabled="false">
<s:layout>
<s:TileLayout />
</s:layout>
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer>
<s:Image id="imageDisplay"
width="75" height="75" source="{data.bmp}" />
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
</s:WindowedApplication>
PictureObject.as
package
{
import flash.display.Bitmap;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.net.URLRequest;
import mx.controls.Image;
[RemoteClass]
[Bindable]
public class PictureObject extends Object
{
protected var _bmp:Bitmap = null;
public function get bmp():Bitmap { return _bmp; }
public function set bmp(newValue:Bitmap):void { _bmp = newValue; }
public function PictureObject(path:String)
{
var imageLoader:Loader = new Loader();
imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
imageLoader.load(new URLRequest(path));
}
protected function onImageLoaded(e:Event):void
{
var imageLoader:Loader = LoaderInfo(e.target).loader;
// create our own copy of the bits in the Loader
var bmp:Bitmap = Bitmap(imageLoader.content);
_bmp = new Bitmap( Bitmap(imageLoader.content).bitmapData );
}
}
}

Show snapshots from camera Flex

I created a flex application that snapshot a picture of the webcam. Im trying now to save every snapshop and display it directly when the image has been capture. But I cant seems to understand how to.
I want the images to be display in the Thumbnail box.
Any help ? or any sites I can found some help on ?
This is what I have for the moment
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" verticalAlign="top"
horizontalAlign="center" paddingTop="0" viewSourceURL="srcview/index.html">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.core.UIComponent;
private function videoDisplay_creationComplete() : void
{
var camera:Camera = Camera.getCamera();
if (camera)
{
videoDisplay.attachCamera(camera);
}
else
{
Alert.show("Oops, we can't find your camera.");
}
}
private function capture_click() : void
{
var snap:BitmapData = new BitmapData(320, 240, true);
var snapBmp:Bitmap = new Bitmap(snap);
snapBmp.width = 320;
snapBmp.height = 240;
if(snapshotHolder.numChildren > 0)
snapshotHolder.removeChildAt(0);
snapshotHolder.addChild(snapBmp);
snap.draw(videoDisplay);
}
]]>
</mx:Script>
<mx:HBox>
<mx:Panel title="Video">
<mx:VideoDisplay id="videoDisplay" creationComplete="videoDisplay_creationComplete();" width="320" height="240" />
</mx:Panel>
<mx:Panel title="Snapshot">
<mx:UIComponent id="snapshotHolder" width="320" height="240" />
</mx:Panel>
</mx:HBox>
<mx:HBox>
<mx:Button label="reload camera" click="videoDisplay_creationComplete();"/>
<mx:Button label="capture" click="capture_click();"/>
</mx:HBox>
<mx:HBox>
<mx:Panel title="Thumbnails">
<mx:UIComponent id="snapshotHolderTN" width="128" height="96" />
</mx:Panel>
</mx:HBox>
</mx:Application>
Pls try with this when u click for image snaps.
var bmp:BitmapData = new BitmapData(videodisplay.width,videodisplay.height);
bmp.draw(drawArea);
var jpgEncode:JPEGEncoder = new JPEGEncoder(50);
var imageByte:ByteArray = jpgEncode.encode(bmp);
var fileRef:FileReference = new FileReference;
fileRef.save(imageByte);

Scroll the datagrid down or up to selected index row flex

I am performing a find operation in a datagrid on one of the columns. After I find the row containing the item, I make that as the selected index row, which highlights it. But now I also want to scroll the datagrid down or up (if the item is out of screen scope) to show that selected item automatically on this find operation.
Thanks.
Have you tried the scrolltoindex() method? Take a look at Anuj Gakhar's article on using scrolltoindex() with a datagrid.
Here's the example from the article:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
initialize="doInit();" creationComplete="setSelectedItem()">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
// this holds the grid data
[Bindable]
private var myData:ArrayCollection = new ArrayCollection();
// change this name here to change the selected item on load
[Bindable]
private var initialFName:String = "Joe9";
private function doInit():void
{
myData.addItem({fname:"Joe",lname:"Bloggs"});
myData.addItem({fname:"Joe1",lname:"Bloggs"});
myData.addItem({fname:"Joe2",lname:"Bloggs"});
myData.addItem({fname:"Joe3",lname:"Bloggs"});
myData.addItem({fname:"Joe4",lname:"Bloggs"});
myData.addItem({fname:"Joe5",lname:"Bloggs"});
myData.addItem({fname:"Joe6",lname:"Bloggs"});
myData.addItem({fname:"Joe7",lname:"Bloggs"});
myData.addItem({fname:"Joe8",lname:"Bloggs"});
myData.addItem({fname:"Joe9",lname:"Bloggs"});
}
private function setSelectedItem():void
{
var gData:Object = dGrid.dataProvider;
for(var i:Number=0; i < gData.length; i++)
{
var thisObj:Object = gData.getItemAt(i);
if(thisObj.fname == initialFName)
{
dGrid.selectedIndex = i;
//sometimes scrollToIndex doesnt work if validateNow() not done
dGrid.validateNow();
dGrid.scrollToIndex(i);
}
}
}
]]>
</mx:Script>
<mx:DataGrid id="dGrid" dataProvider="{myData}" visible="true">
<mx:columns>
<mx:DataGridColumn dataField="fname" headerText="FirstName" />
<mx:DataGridColumn dataField="lname" headerText="LastName" />
</mx:columns>
</mx:DataGrid>
</mx:Application>