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.

Visit Our Friends!

A few highly recommended friends...

Archives

All entries, chronologically...

Pages List

General info about this blog...