Flex Menu Monkey Patch: Allowing a Branch to be Selected
By · CommentsSo the interaction designers decided that the branches of a menu needed to be selectable (picture a warped checkbox tree, only it’s a menu… and there aren’t any checkboxes). The default behavior of a Menu in Flex can be described as follows:
1. If you click on a leaf, the leaf is selected.
2. If you click on a branch, the sub menu opens up.
3. If you hover over a branch, the sub menu opens up.
I needed to change #2 to select the branch.
The first thing that I did was sublcass Menu (we’ll call it NewMenu), and overrode (with copies) two obvious methods on the Menu class:
override protected function mouseUpHandler(event : MouseEvent) : void
override protected function mouseDownHandler(event : MouseEvent) : void
In those two methods you’ll need to remove any expressions containing something similar to the following:
_dataDescriptor.isBranch(item)
This will allow the branches of your sub class to be treated as leaves. The problem is that Menu class contains this line:
private var subMenu:Menu;
which forces all sub menus to be of type Menu– the only NewMenu will be at the very top! We need all sub menus to be NewMenus. So this is where the monkey patching comes in. You have to change subMenu to a protected variable. Then in your NewMenu class, you can override the function:
mx_internal override function openSubMenu(row : IListItemRenderer) : void
And change the line:
menu = new Menu();
to
menu = new NewMenu();
And you’re done! The reason you need subMenu to be protected is because later on in the method you assign menu to subMenu, and you couldn’t do that from a subclass when it was private. Voila. Interaction designers satisfied.
Responding to FocusIn and focusOut Events
By · CommentsI recently had some problems responding to focusIn and focusOut events, trying to handle them from my MXML code. After some experimentation and poking around, I discovered the solution was to override the focusInHandler and focusOutHandler methods. These are protected methods inherited mx.core.UIComponent class.
My particular application was to stop a timer that was triggering events when the control had focus, and to restart the timer when focus was lost. Since you’ll be overriding the method, be sure to call super.focusInHandler(event) to ensure the default behavior is still maintained (unless your reason is to override rather than extend the default behavior). Here are the method signatures:
override protected function focusInHandler(event : FocusEvent) : void
override protected function focusOutHandler(event : FocusEvent) : void
Autoscrolling a Flex TextArea
By · CommentsIn the little suite of console widgets I created, I was having more difficulty than necessary getting my console to scroll to the bottom as text was added. On account of (what I suspect were) timing issues, when I tried executing console.verticalScrollPosition = console.maxVerticalScrollPosition; the maxVerticalScrollPosition has not been updated to reflect the text that had been added.
I was not adding the text directly to the text property of the TextArea, rather it was bound to a String in my model. None of the obvious events fired by TextArea seemed to be doing the trick, the maxVerticalScrollPosition was always what it was previously. However, on the next update it would change to reflect what I wanted it to become after the previous update (in other words it was a step behind).
I solved the problem by adding a binding to console’s text property, like this:
BindingUtils.bindSetter(scroll, this.console, "text");
And the scroll method contained the boilerplate console.verticalScrollPosition = console.maxVerticalScrollPosition; statement. That seemed to do the trick. What I was going to try next was a little more extreme:
BindingUtils.bindSetter(scroll, this.console, "maxVerticalScrollPosition");
but fortunately I didn’t have to.
Data Model Injector (poke)
By · CommentsI needed to build a simple command line interpreter for an application I am building, well, I didn’t need to but I wanted something inconspicuous that the testers (and interaction designers) wouldn’t see and complain about. So I threw something together, it was fun.
Two basic operations I was constantly needing to do was to get a value and/or modify a value. I modified my peek method to return the object in question. And then I also wrote a poke method that allows me to set a value specified by a dot notation.
Here’s the code to the modified peek:
private function peek(prop : String) : * {
try {
var a : Array = prop.split(’.');
var x : * = model;
for (var i : int = 0; i < a.length; i++) {
x = x[ a[i] ];
}
return x;
}
catch (error : Error) { return “undefined”; }
}
You can see it’s just a one line change from my previous implementation. And here is poke:
private function poke(prop : String, val : *) : void {
try {
var a : Array = prop.split(’.');
var x : * = model;
for (var i : int = 0; i < a.length-1; i++) {
x = x[ a[i] ];
}
x[ a[a.length-1] ] = val;
}
catch (error : Error) { trace(error.message); }
}
In this method I had to iterate one less time to get the parent of the object I wanted to assign to. And then on the final line I manually grabbed it to perform the assignment.
My initial wrong assumption was that I could do this in my loop:
for (var i : int = 0; i < a.length; i++) {
x = x[ a[i] ];
}
x = val;
Take a look at it, or step through it in a debugger to see why that doesn’t work. It was pretty enlightening.
Data Model Inpector (peek)
By · CommentsI recently built a console for inspecting arbitrary values contained in my data model. I wanted something similar to the Flex Builder “Variables” window, and though I haven’t arrived there quite yet, I’ll share what I’ve got so far.
The mechanics are a little boring: I extended TitleWindow, there is a TextInput where I enter the value I want to inspect and a TextArea where the ObjectUtil.toString() dump of the object is output.
The heart of the control is this method:
private function peek(prop : String) : void {
try {
var a : Array = prop.split(’.');
var x : * = model;
for (var i : int = 0; i < a.length; i++) {
x = x[ a[i] ];
}
console.text = ObjectUtil.toString(x);
}
catch (error : Error) { }
}
It accepts a “modified-dot-notation” String, parses it, and constructs the result using model as the root.
I knew I was going to use the bracket operator. In fact I started out spliting my tokens and accessing the values like this: model[ a[0] ] [ a[1] ] [ a[2] ]… and so on. It wasn’t very scalable, which is why I was pleased with this method.
The interesting thing about it is that because ArrayCollections allow for use of the bracket operator, accessing elements of an ArrayCollection is simple. Instead of "acName.list.source.2", you can pass the method "acName.2” and it returns the correct value.
Uninstalling IE 8 on Windows Vista
By · CommentsI blindly clicked a link to upgrade to IE 8 this morning. Sadly it was my first Windows Vista experience all over again– seriously, I had some pretty terrifying flashbacks.
Needless to say I couldn’t stand using it for more than 3 hours. The installation process was non-obvious if you think of the browser as an “application,” like I do. If you think of it as part of the OS, like they must at Microsoft, the way is a little more clear.
Here’s how in case anyone else made the same mistake I did (i.e. installing IE 8).
1. Open the control panel.
2. Open “Programs and Features” from the control panel.
3. Click “View installed updates” in the left-hand navigation panel. (This was the trickiest part. I spent a while trying to find it among the other programs before realizing it wasn’t there and changing my approach).
4. If you have a lot of updates, it’s easier to use the “Search” box. Don’t type “IE” in the search box, though, spell it out “Internet Ex…”.
5. Select it, right-click and choose “Uninstall” (it should be the only option)
The rest of the uninstall process was pretty easy (the smoothest part of my IE 8 experience). It required a restart, but IE 7 was back when I logged in.
I might try installing it again later this year, if it’s strill as bad as it was today I might have to make a permanent switch to Firefox (which I’ve been resisting as much as possible).
Update: (4/2/2009) Apparently I overrated the uninstall procedure. For some reason my mouse cursor in IE is always the “grab” cursor. Which means I can’t select text (among other things). This was the straw that broke the camel’s back. I’ve changed my default browser to Firefox. All I can do no is hope that Firefox gets better.
Go Daddy Substrings
By · CommentsI just made an interesting (though unremarkable) discovery. If you check for a domain’s availability at GoDaddy.com, and the domain name you check contains the substring “godaddy” it will prompt you to “enter another domain name”.
Whatever domain name you try– also try it at another registrar and you will see that it’s probably available. I tried “GodAddYou.com” (which was available 15 minutes ago, according to Hostway).
Makes me wonder what other substrings they’re filtering, and what other registrars might be filtering.
This installment will go over setting up messaging in Flex, and assumes everything is working correctly on the server. Recall I will be covering two scenarios. A client-publish/client-subscribe model, and a server-publish/client-subscribe model.
Configuring Channels with ActionScript
I prefer defining my channels and channel sets in ActionScript on the client for a number of reasons. It does away with the need for client side configuration files and additional command line parameters that need to be set up each time I check out the project onto a new machine.
A channel is instantiated as follows:
private var pollChannel : AMFChannel = new AMFChannel(“cf-polling-amf”, http://cfserver.domain.com/flex2gateway/cfamfpolling);
The cf-polling-amf corresponds to the channel definition in the services-config.xml file residing on the server. If you gave it a different name previously, or set up a new one, the names should correspond.
The channel set is instantiated as follows:
private var amfChannelSet : ChannelSet = new ChannelSet();
Then, somewhere inside a method (init(), for example) the channel needs to be assigned to the channel set using the addChannel() method. The channel is now ready to use for remoting or messaging.
Publish/Subscribe
There are three classes you’ll use almost exclusively when dealing with AMF messaging. The Producer class, which connects to the messaging channel and is used to send messages. The AsyncMessage class which defines the format of the messages broadcast via the Producer. And the Consumer, which subscribes to the incoming messages received from the server.
The channelSet and destination properties need to be set on the Producer and Consumer, where the destination corresponds to the destination configured in the messaging-config.xml file on the server.
A MessageFaultEvent.FAULT listener can be attached to the Producer to listen for any errors that occur when connecting. And a MessageEvent.MESSAGE listener should be attached to the Consumer to handle messages that are broadcast to the clients.
Finally the consumer must call it’s subscribe method to indicate it is ready to receive messages. A consumer can subscribe and unsubscribe as often as needed.
Publishing a Message
The Publisher sends message to the server in the form of an AsyncMessage. The AsyncMessage class has two properties that we will be concerned with, the headers property and the body property.
The headers property is a dynamic object that you can add any data you want to. In this case we only need to set up one header value (many more are set after you call send() as it moves up the call stack) which is gatewayid. The gatewayid will correspond to the event gateway you set up in your CF Adminstrator.
The body property on the AsyncMessage class is used to send the message payload, and can contain anything. It is an anonymous object, and can have any number of additional properties or objects attached to it.
After setting these properties, the producer calls send, and passes the message as a parameter. The rest happens on the server and in the method that handles the MessageEvent.
Handling the Message Event
Handling the MessageEvent is similar to creating it, only backward. All of the properties that were set when it was published should be there (unless the CFC gateway explicitly removed or overwrote them). Nested in the event is a body and a headers property. They can be accessed as follows:
event.message.body
event.message.headers
And that’s all there is to it.
Server Publish
As previously mentioned, the server can also publish messages. This is different from the previous scenario in the following ways:
1. You only need to set up a Consumer… no Publisher is needed since the client will not be publishing messages.
2. A CFC on the server needs to manually publish the message. This can happen in a variety of applications.
This is accomplished in ColdFusion by setting up the event object, and then passing it in a call to SendGatewayMessage(). Here is some sample code demonstrating:
<cfset data.message = “someObjectOrValue” />
<cfset data.type = “optionalValue” />
<cfset event.body = data />
<cfset event.Destination = “AValidDestination” />
<cfset ret = SendGatewayMessage(”anEventGateway”, event) />
Other Applications
There are a number of configurations you could have that I haven’t mentioned. A Flex application could publish messages and broadcast to a separate Flex or AIR application that is subscribed to the same channel. There are a variety of methods available to filter messages received.
I’ve only just scratched the surface in getting it set up. What I’m excited for is for an RTMP library to become available, as Adobe announced plans to open source it in the first half of this year. We’ll see how that turns out.
WordPress 2.7 Admin UI
By · CommentsSince updating to WordPress 2.7 I’ve started using ScribeFire almost exclusively. To me the WP admin UI looks as though some “interaction designers” went in and had their way with it. Don’t get me wrong… it’s really nice and it looks great. Alas, almost everything is more pleasant except for writing posts. Had to be interaction designers.
I just went through setting up Flex / ColdFusion messaging via BlazeDS, and have come through it rather unscarred considering it’s my first time using Flex messaging. Most of the documentation I have regarding messaging is biased toward Java (including the sample apps included with BlazeDS). A lot of what I found online, though very helpful, didn’t really apply to the way I wanted to do it. I’m going to document some of the steps here in the hopes that it may be helpful for others (2 parts).
Here are a few details about my configuration that may help you determine if this post might be helpful to you.
- When I create a new Flex project, I always leave the “Application server type” set to “None”. This helps me to create applications that can be used against multiple server types.
- My ColdFusion server isn’t running on the same server I develop on, nor is it usually the same server I serve my production SWFs from.
- This assumes you have BlazeDS correctly set up with ColdFusion, and all of your configuration files (messaging/proxy/remoting/service-config.xml) roughly the same as the default ones included in the BlazeDS distribution (note: there are special ones included for ColdFusion).. If you need help installing and configuring BlazeDS with CF, go here: http://labs.adobe.com/wiki/index.php/BlazeDS:Release_Notes .
- I will be going through 2 scenarios on the client side. First, is a standard publish/subscribe setup, where the clients publish and subscribe. Second, a situation where the client only subscribes, relying on the server to do all the publishing.
- I don’t maintain any of the above mentioned configuration files in the project, electing to manually instantiate my channels/channel sets in ActionScript.
Setting up services-config.xml
The services-config.xml file already contains a channel definition named “cf-polling-amf”. It can be used as-is if you want your poll interval to be 8 seconds. To change it, set the <polling-interval-seconds> tag to the desired interval.
Setting up messaging-config.xml
Next open the messaging-config.xml file. Here is where you set up your adapters and destinations. If the line isn’t included under the <adapters> tag, you will need to add it”
<adapter-definition id="cfgateway" class="coldfusion.flex.CFEventGatewayAdapter" default="true" />
The default destination “ColdFusionGateway” as configured is sufficient for this post. If you set up your own, there are three tags you will need to define:
- <adapter>, this should refer to the adapters defined in messaging-config.xml, usually set to cfgateway.
- <gatewayid>, this can be set as a wildcard (*) meaning the client will define it, or you can set it to a specific gatewayid (which I will explain how to set up shortly)
- <channel>, which refers to one of the channels defined in services-config.xml, in this case “cf-polling-amf”.
Setting up a gateway CFC
An event gateway is tied to a specific CFC file, and is configured in the ColdFusion administrator. First we will set up two CFC classes. One will be used in our client-broadcast/subscribe setup, the other in the server-broadcast/client-subscribe setup.
The basic client-broadcast/subscribe CFC should look like the following:
<cfcomponent output="false">
<cffunction name=”onIncomingMessage” access=”remote” returntype=”struct”>
<cfargument name=”event” type=”struct” required=”true”/>
<cfreturn event.data />
</cffunction>
</cfcomponent>
It’s important that it has that name, and accepts and returns a struct-type. It’s perfectly OK to process the headers or do whatever it is you need to do before the return value is broadcast.
The server-broadcast/subscribe CFC should look like the following:
<cfcomponent output="false">
</cfcomponent>
Complicated, I know, but try not to cry…
Setting up the event gateway
Login to your ColdFusion Administrator and expand the “Event Gateways” tab. From the menu select “Gateway Instances”. Fill in the following fields:
- GatewayID. You will use this either in your messaging-config.xml, and/or in your Flex application.
- Gateway Type. Select “Data Services Messaging”. It should be there if BlazeDS is set up correctly with ColdFusion.
- CFC Path. Enter or browse to the absolute path of the CFC that will handle this gateway’s requests.
Since we’re using the messaging-config.xml, there’s no need to select a configuration file; and leave the startup mode to auto. If you created both CFCs as described above, then you will need to configure a separate gateway instance for each of them.
Once you restart the server, ColdFusion is set up for messaging.

