November 2005 Archives

The Secret to Printing in Flex

| 2 Comments | No TrackBacks

My last Flex project had an emphasis on printing. Printing in Flex can be a difficult beast, but I've managed to tame it with a few simple steps.

First, Lin Lin's tips for printing in Flex are a must read. A lot of what I'm going to say builds off of Lin's advice.

Most of the printing that had to be done in the application involved summary reports. The users had the ability to view summaries in a popup window. These popup window's were TitleWindows that were opened via PopUpManager. Generally, the windows contained a ControlBar with a "Print" and an "OK" button, and had a container with a VScrollBar that displayed the summary information.

Essentially, there were 2 main problems I ran into. First, the content of the container was masked and only the content visible in the popup window was printed (whatever the user was looking at when they clicked print was printed). This was a big problem as the print button needed to print the entire summary regardless of what the user could see via the VScrollBar. Second, there was a gray background that refused to go away no matter what I tried.

The masking problem was fixed with the clipContent property. However, this wasn't straightforward - I had to wrap clipContent in a doLater before printing in order for the change to take effect before the print dialog was displayed. After the print completed, the clipContent property was restored. Here's some sample code:

// Event Handler - called when the print button is clicked
private function onPrintClick ():Void
{
	// Remove the clipping so all of the content is printed
	clipForPrinting( true );
	
	// Start the delay to allow clipping to update before
	// printing anything.
	doLater( this, "doActualPrinting" );
}

// Adjust the clipping to prepare for or recover from print.
private function clipForPrinting( printing:Boolean ):Void
{
	// Assume printing is true if not passed in
	if ( printing == undefined ) {
		printing = true;	
	}

	// Modify the root clipContent so the application's width/height
	// doesn't interfere with bounds of the print content
	Application.application.clipContent = !printing;
		
	// Modify the container holding the content to be printed to remove
	// the scroll masking 
	printArea.clipContent = !printing;
		
}

// Handles the actual printing of the content in the popup
private function doActualPrinting():Void
{
	var printJob:PrintJob = new PrintJob();
	// Keep track of # of pages - only print if there are pages to print
	var pageCount:Number = 0;

	// Show the print dialog
	if ( printJob.start() ) {
		// The user has opted to print - add the pages that
		// need to be printed
		pageCount += PrintUtil.pagenate( printArea, printJob );

		// Send the content to the printer
		if ( pageCount > 0 ) {
			printJob.send();
		}
	}

	// Explicitly delete the printJob
	delete printJob;

	// Fix clipping now that print is done
	clipForPrinting( false );
}

PrintUtil is a class I created to help in splitting up MovieClip's into multiple pages. Essentially, the pagenate method figures out bounds for each page and adds those pages to the printJob passed in via the addPage method, passing in a bounds object. The bounds object contains xMin, xMax, yMin, and yMax properties. After all of the pages have been created, the number of pages successfully added (based on the return value of addPage) is returned.

Solving the gray background color problem turned out to be pretty trivial. A few backgroundColor="#FFFFFF" attributes added to the containers that the print content was in did the trick. This was a bit of trial and error until I found which containers needed the explicitly set white background color.

Taming the printing beast can be tricky, but armed with the above knowledge it should be easier for you. Good luck!

FlashVNC Released

| 40 Comments | 3 TrackBacks

My FlashVNC client is far enough along now for public demonstration. :-) You can download the .swf file here.

It is important to note that due to Flash Player security restrictions in connecting to remote addresses, you have to run the .swf file from your local file system. This means either launching it with the standalone Flash Player, or dropping the .swf file inside IE or FireFox.

Here is a screenshot of FlashVNC in action:

FlashVNC2

For more coverage (and more screenshots!), see the IFBIN news post.

The full FlashVNC source code is included in Flex By Example. The comment-to-code ratio meets IFBIN's quality standards, which means that you should be able to easily decipher what is going on behind the scenes by reading the code comments. Besides just a VNC client, the source code also contains some reusable utility classes and a DES encryption class.

This example is also a work in progress. I've only implemented a few of the encodings - Raw, Rre, CopyRect, and HexTile. I'm currently working on implementing ZLib, Tight and ZLibHex. The Flash Player itself is fully capable of rendering the screen without slowdown.. any choppiness seems to be due to the amount of data coming over the wire, so switching to a compressed encoding format should improve speed. This will be in FlashVNC Alpha 2, which will be posted to IFBIN again when it's complete.

This has only been tested against a TightVNC server, so your mileage may vary. If you encounter any problems, feel free to bug me about them. I know right click doesn't currently work, and I'm trying to decide the best way to handle it. I'm thinking about adding a "Send Right Click" option in the Flash Player context menu that opens which will send the right click event to the server, though I don't like the idea of a right click, mouse move, and left click just to handle right click. The other option I'm thinking about is a user-configurable shortcut key.. like holding down Ctrl+Alt+Shift and clicking.

This is the first of I'm sure many new types of applications that are enabled by Flash Player 8.5. Did I mention I really like ActionScript 3?

Passing parameters to event handlers in AS3

| 10 Comments | No TrackBacks

In AS2 there were several different approaches to passing parameters to event handlers. In AS3, because of method closures there's no longer a need to use Delegate.create for scoping event handlers, which removes the opportunity to send parameters. How, then, can we pass extra arguments to the event handler?

The answer is to create your own custom event types. In AS3, Event is a actually provided to us as a class this time around (as opposed to a vanilla Object with a "type/target/data" properties). We can use this to our advantage, and create subclasses of Event that contain all of the information we want to send along to the event handlers.

Consider my number incrementing example for AS2. We passed the value we wanted to increment by as an extra parameter, and using a better implementation of Delegate (much like Joey Lott's Proxy class) this would be written as such:

// Pass the number 5 to handleIncrement so it knows
// how much to increment by
var handlerFunc:Function = Delegate.create( this, handleIncrement, 5 );

In AS3, what we'll do is actually create an IncrementEvent class. The class will extend Event (because an IncrementEvent is an Event). We'll give it an "amount" property to disignate how much to increment by:

// inside IncrementEvent.as
package {
	import flash.events.Event;
	
	// A event that contains extra information about how much
	// we need to increment by.
	public class IncrementEvent extends flash.events.Event {
		// Rather than use a string for adding event listeners, we use
		// a constant so we avoid typos
		public static const INCREMENT_TYPE:String = "increment";
		
		// The amount we need to incrememnt by
		public var amount:int;
		
		// Create a new event of "increment" type and store the
		// amount we need to increment by
		public function IncrementEvent( amount:int ) {
			super( INCREMENT_TYPE );
			this.amount = amount;	
		}
	}	
}

Now all that's left is to use this Event in an application. Below is some Flex2 markup/code to create an example showing the IncrementEvent in action.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
	xmlns:mx="http://www.macromedia.com/2005/mxml" 
	xmlns="*"
	childrenCreated="addListeners();">
	
	<mx:Script>
		<![CDATA[
			// Running total that we'll display in the text field
			private var value:int = 0;
			
			// When the app runs, we add listeners for the increment event so
			// we can update value whenever a button is clicked
			private function addListeners():Void {
				increment1.addEventListener( IncrementEvent.INCREMENT_TYPE, handleIncrement );
				increment5.addEventListener( IncrementEvent.INCREMENT_TYPE, handleIncrement );
			}
			
			// Update the value by adding the amount specified in the increment event
			private function handleIncrement( ie:IncrementEvent ):Void {
				value += ie.amount;
				display.text = value.toString();
			}
			
		]]>
	</mx:Script>
	
	<mx:HBox>
		<mx:VBox>
			<!-- Whenever a button is clicked, fire off an increment event with the
				appropriate incrememnt amount -->
			<mx:Button id="increment1" label="+ 1" click="increment1.dispatchEvent( new IncrementEvent( 1 ) );" />
			<mx:Button id="increment5" label="+ 5" click="increment5.dispatchEvent( new IncrementEvent( 5 ) );" />
		</mx:VBox>
		
		<mx:Label text="Value:" />
		<!-- Bind text to value so that whenever value changes we see it update in the UI -->
		<mx:Text id="display" text="0" />
	</mx:HBox>
	
</mx:Application>

Notice how the handleIncrement method now takes an IncrementEvent instead of plain old Event like it did for AS2. The IncrementEvent contains the extra information that we would've normally passed in a Delegate.create call.

Sure, it requires a bit more code to accomplish the same effect.. but in the end you wind up with more structure and the code communicates its intent much better (which is a basic idea of Extreme Programming).

To continue this example, I'll be adding an IncrementButton that will do away with the click handler and dispatching of the IncrementEvent. Instead, it will use an "increment" attribute, allowing us to write: <IncrementButton increment="5" label="+5" />, which is a cleaner implementation. Look for this in Flex By Example.

Flex.org - The Directory for Flex

Archives