A simple Flex drag handle.

Jun 27
2009

I had a need for the user to be able to manually drag and re-organise some windows on the screen. This is a fairly common problem I think, but I couldn’t find any simple examples that didn’t involve messing around with the DragManager. I don’t really need that kind of flexibility, I’m quite happy for the draggable items to be constrained within a single container.

Each of my draggable components is going to be a container itself, so I want a UIComponent I can add to the display list which will just automagically deal with moving the parent around when we click and drag. i.e. in MXML I want to write something like this :

<Application 
    xmlns="http://www.adobe.com/2006/mxml" 
    xmlns:local="*"
    layout="absolute">
 
    <VBox>
        <local:DragHandle height="20" width="100%"/>
        <!-- More components go here -->
    </VBox>
</Application>

The intent being that this should enable me to drag that VBox anywhere within the main application window without adding any further code to either the VBox or the application.

First things first, I need to inherit from UIComponent and add a simple sprite to act as the visible handle :

package
{
    import flash.display.Graphics;
    import flash.display.Sprite;
 
    import mx.core.UIComponent;
    import mx.events.ResizeEvent;
 
    public class DragHandle extends UIComponent
    {
        private var _rect:Sprite;
        public function DragHandle()
        {
            super();
            _rect = new Sprite();
            addChild(_rect);
 
            addEventListener(ResizeEvent.RESIZE, draw);
        }
 
        private var _fillColor:Number = 0xcccccc;
        public function set fillColor(value:Number):void
        {
            _fillColor = value;
            invalidateProperties();
        }
 
        protected override function commitProperties():void
        {
            super.commitProperties();
 
            draw(null);
        }
 
        private function draw(e:ResizeEvent):void
        {
            var g:Graphics = _rect.graphics;
            g.clear();
            g.beginFill(_fillColor, 1);
            g.drawRect(0, 0, width, height);
            g.endFill();
        }		
    }
}

This just draws a rectangle and positions it at (0,0) within the UIComponent. We handle the ResizeEvent so that the sprite stays the same size as the UIComponent container.

You may wonder why I’m adding a Sprite to the UIComponent and not just using the underlying Sprite of the UIComponent class. Well, for reasons which are currently unclear to me, I can’t seem to get UIComponent to fire the MouseDown event. Perhaps I’ll come back to this and investigate later – for now I’ll use the way I know that works. I don’t think there’s anything wrong with approaching a project like this. After all, it’s generally more important to get something working than it is to write something elegant. I could find, after a few hours of investigation that for some arcane reason I have to do this anyway, so, for now, I’ll let it stand.

Next we need to handle the Sprite’s MouseDown event, so we declare an event handler in our DragHandle constructor and then add this function definition :

private function handleMouseDown(e:MouseEvent):void
{			
    systemManager.addEventListener(MouseEvent.MOUSE_MOVE, doMove, true);
    systemManager.addEventListener(MouseEvent.MOUSE_UP, endMove, true);
 
    _window = this.parent as UIComponent;
 
    //We bring the window to the top of the parents display list
    //so that it passes in front of all other display objects.
    var _parent:DisplayObjectContainer = _window.parent; 
    _parent.setChildIndex(_window, _parent.numChildren - 1);
 
    _originalPosition = new Point(_window.x, _window.y);  
    _mouseDownPosition = new Point(e.stageX, e.stageY);  
}

This adds two event handlers for MOUSE_MOVE and MOUSE_UP. The former will actually move the handle container (what we’re calling _window, here) and the latter will disconnect both of these event handlers, ending the drag process :

private function doMove(e:MouseEvent):void
{
	e.stopImmediatePropagation();
 
	var positionToMove:Point = new Point(
		_originalPosition.x + (e.stageX - _mouseDownPosition.x),
		_originalPosition.y + (e.stageY - _mouseDownPosition.y));
 
	if (positionToMove.x < 0) positionToMove.x = 0;
	if (positionToMove.y < 0) positionToMove.y = 0;
 
	_window.move(positionToMove.x, positionToMove.y);
}
 
private function endMove(e:MouseEvent):void
{
	e.stopImmediatePropagation();
	systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, doMove, true);
	systemManager.removeEventListener(MouseEvent.MOUSE_UP, endMove, true);
}

And that’s pretty much it. You can see a demo (and source code) here.

Flex widget communication PoC

May 29
2009

Problem : How do you get several distinct Flex widgets on a page to communicate?

By distinct here I mean that each widget is a separate embedded SWF file, i.e. they don’t share the same Application.

There are actually several ways to do this, and an appropriate choice really depends on the kind of data you want to share and when. What I needed was real-time communication; say I have one widget that searches for a particular resource (in my case they’re bonds), when the user selects a resource, that widget should propagate the selection to all the others on the page. It was also a requirement that the number and identity of the widgets on the page be an unknown – i.e. I can’t assume that there will always be exactly three widgets called Fred, Bob and Jane.

Not being a Flex expert, my first thought was to use a SharedObject. This seemed like a good idea since apparently they “offer real-time data sharing between multiple client SWF files”, which is exactly what I want. However, this is only possible if you’re using Flash Media Server. I don’t really need that round-trip to the server each time and I don’t need to communicate between multiple clients, just among SWF files on the same page at a single client. I also don’t have Flash Media Server. I did toy around briefly with a polling solution using local SharedObjects though. The server SWF would contain something like this :

public function syncSharedObject(sharedText:String):void
{
    var so:SharedObject = SharedObject.getLocal("test", "/", false);
    so.data.sharedText= sharedText;
    so.flush();
}

And the client SWF would contain code to poll the SharedObject periodically to check for updates (I’m not going to show the boilerplate code that sets up the Timer instance):

public function syncSharedText(event:TimerEvent):void
{
    var so:SharedObject = SharedObject.getLocal("test", "/", false);
    sharedText = so.data.sharedText;
}

Simple! But also fairly braindead. This is a pull architecture – the clients aren’t notified when the data has changed, so they have to keep looking at it (say every half second), which creates some annoying overhead – each widget interested in “sharedText” would have to poll the SharedObject and that could be a lot of widgets looking at one piece of data. Of course, this gets worse if we need to pass around lots of different pieces of data!

So, back to the drawing board and a little more reading, after which I discovered LocalConnection. This seems to do pretty much everything I need, but it’s definitely more complex to get up and running with. What I want is one widget to act as a server and notify all the other widgets about changes to relevant data, however I can’t designate a specific widget as the server because I don’t know if it will always be present. A simple solution would be to let the first widget to load grab the publisher/server functionality and all subsequently loaded widgets would then be subscribers/clients (I decided to call the class MessageManager, and, since it will need to raise events, it extends EventDispatcher) :

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//constructor (I'm not going to write out all the class definition code)
public function MessageManager(target:IEventDispatcher=null)
{
    //Generate a locally unique ID.
    this.subscriberID= UIDUtil.createUID();
 
    try
    {
        //First we try to connect as the server.
        conn = new LocalConnection();
        conn.connect("Server");
        conn.client = this;
        isServer = true;
 
        //initialise the subscriber cache.
        subscribers = new Object();
    }
    catch (e:Error)
    {
        //The server probably already exists; register ourselves with it.
        isServer = false;
        conn = new LocalConnection();
        conn.connect(subscriberID);
        conn.client = this;
        conn.send("Server", "registerSubscriber", this.subscriberID);
    }
 
    super(target);
}
 
public function registerSubscriber(subscriberID:String):void
{
    if (!subscribers[subscriberID])
         subscribers[subscriberID] = true;
}

There are a fair few assumptions in this code (there’s no real error handling, for a start), but it works well enough for a PoC. It may seem a little confusing to use the same class for both publisher and subscriber interfaces, and it would be entirely feasible to separate them out, but I didn’t consider that necessary for such a small scale test.

With the publisher and subscribers loaded and registered, I now need to be able to send a message. I created a MessageManagerEvent class which extends Event with an additional data property, this contains the “message” to be passed around (a fairly common pattern – the Flex store sample application uses something similar but calls it ObjectDataEvent). For a widget to then update all other widgets, it just needs to create a MessageManagerEvent and call the MessageManager.sendMessage method. sendMessage determines whether the current instance of MessageManager is the server or a subscriber and either sends a single message to notify the server or iterates over the subscribers collection notifying each one in turn. I created a simple application to demonstrate this in action and included two of them below (you should be able to view source on either) :

Just enter some text into either box and you should see it copied across to the other application.

If you refresh the page a few times, you may notice the “SERVER” and “SUBSCRIBER” labels flip back and forth between the widgets, depending on which one loads first. If you open a new copy of this entire page, the new instances should both be saying “SUBSCRIBER” (assuming you keep this page open). Even more interesting is that if you open a copy of this page in another browser entirely, you may find that those say “SUBSCRIBER” as well, and that text is propagated to them from the original browser instance! (I’ve noticed this works between FireFox and IE so far, YMMV, of course).

A few points to note :

  • You can use more complex objects as messages, but each widget must have a reference to that type compiled in, otherwise it won’t deserialise correctly.
  • Any type used as a message must have a default constructor, or at least a constructor with default parameters.
  • This is far from being a usable implementation, since it doesn’t account for the possibility of the server or any subscriber dying. Ideally there would be a mechanism for a subscriber to take over the server role if the server is removed, though this would probably require use of a SharedObject to persist the subscriber list.

Update

What I’ve done here was for my benefit really – a learning exercise. What Blitz Agency has done here is something on another level altogether. Under the hood it’s all still LocalConnection based though.

Visit Our Friends!

A few highly recommended friends...

Archives

All entries, chronologically...

Pages List

General info about this blog...