return undefined;


Simple monkey patch to fix ToolTipManager.toolTipClass

Posted in AS3, Flex by Ben on the July 3rd, 2008

ToolTipManager.toolTipClass doesn't work in Flex 3.0 out of the box. While its intent of allowing you to specify a custom IToolTip implementation is great, ToolTipManagerImpl simply acts as if the property doesn't exist and instantiates an instance of ToolTip. Luckily, the fix is quite easy if you are willing to do a bit of monkey patching. You just have to make a slight modification to the createToolTip() method, making it look like the following:

public function createToolTip(text:String, x:Number, y:Number,
                                     errorTipBorderStyle:String = null,
                                     context:IUIComponent = null):IToolTip
{
    // ***** USE THE PROPERTY! *****
    var toolTip:IToolTip = new ToolTipManager.toolTipClass();

    var sm:ISystemManager = context ?
                            context.systemManager :
                            ApplicationGlobals.application.systemManager;
    sm.toolTipChildren.addChild(UIComponent(toolTip));

    if (errorTipBorderStyle)
    {
        UIComponent(toolTip).setStyle("styleName", "errorTip");
        UIComponent(toolTip).setStyle("borderStyle", errorTipBorderStyle);
    }

    toolTip.text = text;

    sizeTip(toolTip);

    toolTip.move(x, y);
    // Ensure that tip is on screen?
    // Should x and y for error tip be tip of pointy border?

    // show effect?

    return toolTip;
}

Here is a diff of the base and updated classes.

As an aside, an easier fix is said to exist here, but it doesn't seem to work when calling ToolTipManager.createToolTip() directly, which is what I needed to do. If you are using toolTips normally by just setting the toolTip property I would recommend trying Peter's fix first.

I would also like to go on record that I hate the *Impl naming convention. Despite what Theo says, I like my interfaces with I's. *Impl just seems kludgy and redundant, and it reminds me of AS2.

flexmdi sans MXML

Posted in AS3, Flex, flexmdi by Ben on the March 11th, 2008

Every now and then I come across a request of how to use FlexMDI using only ActionScript. Its completely straightforward but I have finally gotten around to getting an example up. The first code snippet shows how to do this while still utilizing MDICanvas for some of the convenience it provides.

import flexmdi.containers.MDICanvas;
import flexmdi.containers.MDIWindow;
import flexmdi.effects.effectsLib.MDIVistaEffects;

private function init():void
{
    var mdic:MDICanvas = new MDICanvas();
    mdic.percentWidth = mdic.percentHeight = 100;
    mdic.effects = new MDIVistaEffects();
   
    var win1:MDIWindow = new MDIWindow();
    win1.title = "First Window";
    win1.width = 300;
    win1.height = 200;
    mdic.addChild( win1 );
   
    var win2:MDIWindow = new MDIWindow();
    win2.title = "Second Window";
    win2.width = 300;
    win2.height = 200;
    win2.x = 325;
    win2.y = 50;
    mdic.addChild( win2 );

    var btn:Button = new Button();
    btn.label = "Awesome Button";
    win2.addChild( btn );
   
    addChild( mdic );
}

This next example shows how to turn a basic Canvas component into the container for an MDI implementation.

import flexmdi.containers.MDICanvas;
import flexmdi.containers.MDIWindow;
import flexmdi.effects.effectsLib.MDIVistaEffects;
import flexmdi.managers.MDIManager;

import mx.containers.Canvas;

private function init():void
{
    var canvas:Canvas = new Canvas()
    canvas.percentWidth = canvas.percentHeight = 100;
   
    var mgr:MDIManager = new MDIManager( canvas, new MDIVistaEffects() );
   
    var win1:MDIWindow = new MDIWindow();
    mgr.add( win1 );
    win1.title = "First Window";
    win1.width = 300;
    win1.height = 200;
    win1.x = win1.y = 10;
   
    var win2:MDIWindow = new MDIWindow();
    mgr.add( win2 );
    win2.title = "Second Window";
    win2.width = 300;
    win2.height = 200;
    win2.x = 350;
    win2.y = 100;

    var btn:Button = new Button();
    btn.label = "Awesome Button";
    win2.addChild( btn );
   
    addChild( canvas );
}

Hopefully this clears up some of the uncertainty around how to use FlexMDI without our good friend MXML but feel free to ask questions in the comments.

Creating bindable, calculated read-only properties in Flex

Posted in AS3, Flex by Ben on the March 7th, 2008

Binding to read-only properties in Flex takes a bit more work than one might think at first glance. There are two basic types of read-only properties in Flex: "variable backed" and "calculated". Can you guess which one we're going to discuss here?

Lets start by clarifying exactly what we mean by read-only property. The most basic description is when you have a getter but no setter:

public function get someValue():Number {...}

The variable backed version is when the getter is just a gatekeeper for a private variable:

private var _someValue:Number;

public function get someValue():Number
{
   return _someValue;
}

The calculated version is a bit more complex and is generally either a combination of values like first name and last name or a value that requires calculation. The example we'll use here is a totalTime property of a Playlist. The totalTime is calculated by adding together the duration of all songs in the playlist at any given time.

OK, how?

By default you will get a warning telling you that the binding on a read-only property will be ignored (and whatever you've bound to it will indeed not receive any updates). The first part of enabling the bindings involves using metadata. Everybody knows [Bindable], but what we're looking for is [Bindable(event="eventName")]. That will essentially tell the compiler to dispatch a PropertyChangeEvent for the property (which is how binding works) any time an event of type "eventName" is fired. That brings us to the next step of firing the event. In our scenario of a calculated read-only property this needs to happen when one/any of our inputs changes.

Going back to the playlist example, a good time to let everyone know totalTime has changed is when a song is added. For the sake of simplicity that is the only scenario this example covers. I won't bother talking through the code in the example because it is very straightforward.

View the example and view the source and you'll see this is all very simple. I have included the classes generated by the compiler as well in case anyone wants to dig around. The magic seems to be injected from _CalculatedBindableReadOnlyWatcherSetupUtil.as but I won't claim to have a very solid understanding of how that all works.

I hope this will shed some light for somebody out there and maybe even come in handy if you're stuck. Binding to variable backed read-only properties has been covered by numerous other people and I would recommend learning about that as well. Enjoy!

Hidden gem of the Flex framework: LayoutContainer

Posted in AS3, Flex by Ben on the January 8th, 2008

Every now and then I stumble across something in the Flex framework that I am both surprised and sorry I didn't know about sooner. Like the fact that CheckBox has a built-in clickHandler method. Duh. LayoutContainer just may be one of the most useful classes around, especially if you're creating components you want to be reusable and flexible. First, a little background.

Its fairly common knowledge that VBox and HBox both extend Box and do nothing more than set a direction property. What may be a bit less obvious is the fact that the direction property is actually applied to an internal layoutObject property. In the case of Box (and by extension VBox and HBox), layoutObject is of type BoxLayout. As it turns out, Canvas uses an internal layoutObject as well, but in that case its an instance of CanvasLayout. Now notice that my references to both BoxLayout and CanvasLayout are not linked to the corresponding documentation. This is because Adobe has specifically excluded them from the documentation because, for whatever reason, Joe Developer isn't supposed to directly use them.

When I first discovered this I set about using them anyways because I had been struggling to find an optimal solution to a problem for a couple of days. Sure they always say things "may not be supported in future versions" but when was the last time they actually pulled something out and besides, where's your sense of adventure? I was on my way to creating a base class that could act like a Canvas, VBox or HBox using some intelligent switching between what type of layoutObject (and direction) it used. I was happy. Well, I didn't get far before I started hitting errors due to not implementing some deeply nested interface or some other nonsense. I was no longer happy. Thankfully, while digging through source and documentation I stumbled onto LayoutContainer. Lo and behold it does exactly what I was trying to accomplish! By changing the layout property between "absolute", "vertical" and "horizontal" (defined as constants on ContainerLayout), you can have your container lay its children out like a Canvas would (direction = ContainerLayout.ABSOLUTE) or like a Box would (direction = ContainerLayout.VERTICAL | ContainerLayout.HORIZONTAL). I suppose this is only exciting if you have a need for this kind of functionality, which, who knows how common that is but it was an absolute life saver on my recent task.

Stay tuned for where this technique is getting applied but I will leave you with the chunk of code from LayoutContainer that does the magic referenced in this post.

public function set layout(value:String):void
{
    if (_layout != value)
    {
        _layout = value;

        if (layoutObject)
            // Set target to null for cleanup.
            layoutObject.target = null;

        if (_layout == ContainerLayout.ABSOLUTE)
            layoutObject = new canvasLayoutClass();
        else
        {
            layoutObject = new boxLayoutClass();

            if (_layout == ContainerLayout.VERTICAL)
            {
                BoxLayout(layoutObject).direction =
                    BoxDirection.VERTICAL;
            }
            else
            {
                BoxLayout(layoutObject).direction =
                    BoxDirection.HORIZONTAL;
            }
        }

        if (layoutObject)
            layoutObject.target = this;

        invalidateSize();
        invalidateDisplayList();

        dispatchEvent(new Event("layoutChanged"));
    }
}

While the implementation may not be rocket surgery it is extremely useful at times. In fact, you may have even used one of LayoutContainer's subclasses before, its a little class called Application.

Efficient, reusable (and centered) CheckBox renderers for DataGrids

Posted in AS3, Flex by Ben on the November 16th, 2007

As it turns out, truly reusable CheckBox renderers are much simpler to create than I previously reported. This post will provide and discuss what I think are the last CheckBox renderers you will ever need.

Did you know that CheckBox has a built-in click handler? I didn't until recently, despite its not-so-sneaky name of clickHandler. It is a protected function, so its only accessible if you subclass CheckBox, but that's OK since our renderers will do just that. clickHandler is key to creating these reusable renderers and keeping them super simple.

Before I start in on my inevitably wordy and detailed description lets take a look at our end result. CheckBoxRenderers in action and the source.

Edit: I initially forgot to mention that these renderers are heavily based on and influenced by the information provided by Flex Jedi Alex Harui on his blog. Thanks to Alex for illustrating "the right way" to do things.

This time around we will start with the item renderer since it is the simpler of the two. (Both of these classes center the CheckBox as that is the most common request, but if you don't need them centered simply remove the updateDisplayList() functions.)

CenteredCheckBoxItemRenderer.as:

package com.returnundefined.view.renderers
{
    import flash.display.DisplayObject;
    import flash.events.MouseEvent;
    import flash.text.TextField;
   
    import mx.controls.CheckBox;
    import mx.controls.dataGridClasses.DataGridListData;

    public class CenteredCheckBoxItemRenderer extends CheckBox
    {
        // update data item on click
        override protected function clickHandler(event:MouseEvent):void
        {
            super.clickHandler(event);
            data[DataGridListData(listData).dataField] = selected;
        }
       
        // center the checkbox icon
        override protected function updateDisplayList(w:Number, h:Number):void
        {
            super.updateDisplayList(w, h);
           
            var n:int = numChildren;
            for (var i:int = 0; i <n; i++)
            {
                var c:DisplayObject = getChildAt(i);
                // CheckBox component is made up of box skin and label TextField
                if (!(c is TextField))
                {
                    c.x = (w - c.width) / 2;
                    c.y = (h - c.height) / 2;
                }
            }
        }
    }
}

There are 2 major differences between this class and our previous version. First, it doesn't implement ClassFactory, which means we can simply type its fully qualified class name into the itemRenderer attribute of a DataGrid and it will work (just like with mx.controls.CheckBox), requiring one less bindable variable in our file. The reason we can do this is that CheckBox, and by extension CenteredCheckBoxItemRenderer implement IDropInListItemRenderer. I won't go into the full details (if anyone would like me to dedicate a post to describing this more fully let me know) but in the case of CheckBox, that basically means that it knows how to set its selected state based on a piece of data that is passed to it. The second major difference is closely related to this fact, which is that we no longer have to override the data setter- we get that functionality for free.

The most important line in the item renderer class is essentially the other half of the equation. Whereas we get data retrieval and rendering for free, we have to provide the data setting functionality. That is really the only thing that separates it from a regular CheckBox other than the centering code, and its accomplished with this single line of code:

data[DataGridListData(listData).dataField] = selected;

This line uses the listData property (defined by IDropInListItemRenderer) to locate and update the specific piece of data this CheckBox is rendering in our DataGrid.

The CenteredCheckBoxHeaderRender class is pretty similar to my previous version in that it still implements IFactory and has to be implemented as a ClassFactory instance. For details on using ClassFactory see the previous post referenced above. The main differences this time around are that the class is defined in ActionScript rather than MXML and streamlines things by using the built-in clickHandler function. Below is the portion of the class we will discuss further.

// these vars are used to reference the external property that stores our selected state
public var stateHost:Object;
public var stateProperty:String;

// set selected state based on external property
override public function set data(value:Object):void
{
    selected = stateHost[stateProperty];
}

// toggle external property on click
override protected function clickHandler(event:MouseEvent):void
{
    super.clickHandler(event);
    stateHost[stateProperty] = selected;
}

As discussed in the previous post, headerRenderers get initialized repeatedly during their existence. This means that we are unable to store any kind of state for them inside themselves because it will get reset over and over again. To work around this we define the stateHost and stateProperty variables which will be assigned via the properties property of ClassFactory. (Again, see previous post for a more thorough explanation of ClassFactory.) In our example application we point these to a selectAllFlag variable (the stateProperty) defined in the main application (the stateHost property). The overridden data setter shown above then uses this external value to set the selected state of our component. Correspondingly, clickHandler sets this value when our CheckBox is clicked, saving our state out to a safe, external location.

Two minor things to mention about our application are a couple of attributes set on the DataGridColumn that our renderers live in. First, notice that we set a dataField attribute like usual. This is required for our new itemRenderer class to work properly, whereas it was not needed when using ClassFactory for both the headerRenderer and itemRenderer. The second important point is that we have set the column's sortable attribute to false. This is necessary in order to ensure clicks in the header are processed as CheckBox clicks and not sorting actions. Also note that we process the header click and set the item values in the main file. While it would be possible to do this inside the header renderer class leaving it outside makes the renderer that much more flexible.

Hopefully this all makes sense, is not too long winded and provides renderers that others can and will use in future projects. Please post questions, complaints and comments in the... well... comments.

CellularDataGrid for Flex 2

Posted in AS3, Flex by Ben on the November 9th, 2007

A DataGrid that supports highlighting and selection on a cellular level (as opposed to row level like the default) is a fairly common request/topic. While I realize people have done this before and this is supported out of the box by Flex 3 (sort of, see first comment below) I decided to give it a shot during some down time yesterday. I was pretty amazed at how easy it was. Even in my newline-happy code format the component is only 55 lines. The results of about 2 hours of fiddling can been seen and downloaded by following the link below.

View CellularDataGrid (Right click for source)

Hopefully someone finds this useful and/or interesting. If nothing else it should serve as an example of why diving into the framework and getting your tweak on is a worthwhile activity.

Update: Thanks to Tom's suggestion, the problem with the selection indicator not disappearing correctly when selecting a cell in the same row is now fixed. The example and source have been updated.

flexmdi: Customizing default behaviors

Posted in AS3, Flex, flexmdi by Ben on the September 14th, 2007

flexmdi handles a lot of the mundane tasks of an MDI interface for you: minimizing, maximizing, closing, etc. However, we also realize that the out of the box behavior will not always fit your exact needs. Consequently, one of our biggest architectural goals (and challenges) was how to provide enough default behavior that getting up and running was lightning fast while still allowing the fine grained control a truly useful project provides. I described the basic method we used in a previous post, but wanted to give an example more specific to flexmdi now that its live.

The example we always thought of during development was requiring a user to confirm that they want to close a window before it is actually removed, like this (view source enabled). Any listeners you set on MDIWindow or MDIManager will be called before the internal listeners that execute the default behavior. As long as you don't give your listener a negative priority that is, so don't do that if you want first dibs. To prevent the default behavior from executing, you simply call event.stopImmediatePropagation() on the event passed into your listener. If you might want to execute that default behavior in the future, such as after the user confirms they want to close the window, you need to store the event somewhere. Let's look at some sample code (assumes you're listening on the manager in order to catch events for all of the windows):

private function confirmWindowClose(event:Event):void
{
    if(event is MDIManagerEvent)
    {
        // this is the line that prevents the default behavior from executing as usual
        event.stopImmediatePropagation();
        // store the event in case we want to resume later (user confirms their intention)
        queuedEvent = event as MDIManagerEvent;
        // ask user to confirm
        Alert.show("Seriously? Close it?", null, 3, null, handleAlertResponse);
    }
}

So now we have captured the event, put it off to the side and prompted the user to make sure they really really really want to close the window. handleAlertResponse() will be called when the user clicks on one of the buttons provided by our Alert. Here is what that method looks like:

// if the user said yes, we execute the default behavior of playing an effect
// and then removing the window by calling the appropriately named executeDefaultBehavior() method
private function handleAlertResponse(event:CloseEvent):void
{
    if(event.detail == mx.controls.Alert.YES)
    {
        mdiCanvas.windowManager.executeDefaultBehavior(queuedEvent);
    }
}

As you can see, if the user told us to do so we go ahead and "resume" the default behavior (playing an effect and removing the window) by calling MDIManager.executeDefaultBehavior() and passing it the event we previously stored. Thats it. You now have an MDI interface that requires confirmation when closing windows.

Because executeDefaultBehavior() contains a switch that examines the event's type, you can use this exact approach to prevent and/or modify default behaviors for any event flexmdi uses. I think confirming window close would definitely be the most common usage, but I'm sure its not the only one. For example, you could intercept the windowMaximize event and fade all other windows back or minimize all other windows before animating the window to its maximized size/position.

As always, questions, comments, complaints, etc are welcome. I would love to get feedback on this and hear if you think its easy enough/will suit your needs/could be improved/will end world hunger.

Announcing flexmdi: Robust, extensible MDI framework for Adobe Flex

Posted in AS3, Flex, flexmdi by Ben on the September 11th, 2007

One thing the Flex community has lacked up to this point is a robust, extensible, open source solution for creating MDI interfaces. Today that is changing as myself (Ben Clinkinbeard), Brian Holmes and Brendan Meutzner are excited to announce flexmdi. As the name and my introduction implies, flexmdi is a framework for easily creating MDI interfaces in Adobe Flex. While you can literally get up and running with flexmdi in a matter of seconds, our team focused heavily on allowing developers to easily extend and customize the framework to fit it to their specific needs.

The project has been constructed during nights and weekends over the past month or so since we all have day jobs, but the focus has always been on creating a framework that enables developers to be productive out of the gate while allowing powerful customizations by way of an intuitive and robust API. Some highlights include:

  • Draggable, resizable (from any edge/corner) windows
  • Default functionality for minimize, maximize/restore and close
  • Extensive event model on both a window and manager level
  • Externalized effects classes for transitions (allows developers to create their own or extend from our base implementations)
  • Cascade, tile and tile plus fill space window management
  • Context menu functionality
  • Ability to modify/customize default behaviors, not just override them (though you can do that too)
  • Construct UIs in MXML or AS

We have a few articles/tutorials put together already, and more are on the way. This is a pretty solid 1.0 release, but we will also be polishing and cleaning things between now and MAX, where we will be giving a short demo during the Flex Boot Camp session. Please send us your feedback! We are eager to hear from other developers about what is good, what is bad, what should be easier, what use cases you would like support for, etc, etc.

So now, onto the links:

Thats all for now, but expect ongoing updates and articles. And once again, got a complaint? Tell us! Like what you see? Tell us! Want to contribute (code, not money) to the project? Tell us!

Enjoy!

PS - Quick shout out to Wietse Veenstra. We used his stylesheet as a starting point and I believe are still using his images for the window controls buttons. Thanks!

Custom Flex components: Providing default yet overridable behavior

Posted in AS3, Flex by Ben on the September 7th, 2007

A good component provides enough out-of-the-box capability that getting up and running with it is quick and easy. An even better component allows for the flexibility of overriding and/or customizing that default behavior. As it turns out, providing this flexibility in your components is quite easy. This article will explain how to do just that.

Since Flex uses an event based programming model, the addEventListener() method is one of the foundational aspects of Flex development. The fourth parameter of this method is priority. What this does is allow developers to "rank" their listeners and specify in which order they should be executed. Listeners are called in descending order, so a listener with a priority of 2 will be called before a listener with a priority of 1, which will be called before a 0 priority listener, etc. It should be noted, however, that listeners will not necessarily finish executing before the next one in the queue is called. The default priority is 0, as evidenced by addEventListener()'s signature:

addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

The priority argument is the key to providing default yet overridable/customizable behavior in your custom components. The basic approach, in a nutshell, is to have your component listen for its own events, but with a priority of -1 (or any negative number, really). By doing this, you let users of your component (other developers) listen for the events externally as normal, but also give them the option of preventing the event from ever reaching your default internal listeners. The Event class provides a stopImmediatePropagation() method that prevents the event from being dispatched to any listeners with a lower priority. So if you add a listener with the default priority of 0 (or any positive number) and call event.stopImmediatePropagation(), the event will never reach the default internal listener, since it has a negative priority.

Here is an example, with view source enabled. The project that necessitated figuring this stuff out will hopefully be going live early next week, so stay tuned. :)

Update: The project is live!

“Apollo” to be called Adobe AIR?

Posted in AS3, Flex, pointless blather by Ben on the June 8th, 2007

Everyone knows that Apollo is just an interim name for Adobe's upcoming platform for desktop RIAs. Today Ted Patrick concluded his week of posts revealing features of Flex 3 with a post on "Apollo". Aside from the liberal use of quotes, he also blurred out what we can assume is the official name that the product will ship with in the screenshots he provided. Below you will find a magnified portion of one of the images that seems to clearly show the name beginning with an A, and being very short. The final letter also has what looks an awful lot like the descender of a capital R. I suppose AIR would make a pretty suitable name since the application files the platform generates are .air files, and of course its RIA backwards. What do you think?

Apollo name revealed?

Next Page »