Monday, June 27, 2011

Null FocusManager, broken tabbing and exploding drag and drop with PopupAnchor in Flex 4

I was happily working away today with a pair of Tree components, implementing drag and drop. In my test harnessing, everything was going swimmingly, until I dropped my popup window into an anchor component. Egads! It s'ploded!

 Source Code as Zip

ListBase, line 8619:
DisplayObject(dropIndicator).parent.removeChild(DisplayObject(dropIndicator));



TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mx.controls.listClasses::ListBase/hideDropFeedback()[E:\dev\hero_private\frameworks\projects\mx\src\mx\controls\listClasses\ListBase.as:8619]
at mx.controls::Tree/dragEnterHandler()[E:\dev\hero_private\frameworks\projects\mx\src\mx\controls\Tree.as:3210]
at flash.events::EventDispatcher/dispatchEventFunction()


What to do, what to do?

well, parent is null, but if you put a watch on this.focusManager, you'll see it's null. Wow, that's bad, huh?

Well, after adding a bit of thuggery, buggery and skullduggery, and by that I mean by manually injecting the focusManager into the child component, I saw that drag/drop worked. So why the dickens does an anchor drop the focusManager?

Weeelll... it seems that if you look closely at my code below, I had used a HGroup to bundle my components together. This is not what you want to use, as you really need a skinnableContainer. Why? Because it extends SkinnableContainerBase, which implements IFocusManagerContainer. If you look very closely, you'll see that in fact, IFocusManagerContainer's functional contract is actually implemented in UIComponent. Very sneaky.

How to prove my theory? Well, wrap the HGroup in a BorderContainer, and voila! No more explosions, earthquakes, fires, floods or tidal waves. Just a working drag and drop tree pair in a popup window.

  id="popupAnchor">    <classes:PopupWindow id="popupWindowinPopup" width="200" height="200" selectedItems="{selectedItems}" dataProvider="{dataProvider}"/> </s:PopUpAnchor>
vs

id="popupAnchor">    <s:BorderContainer>       <classes:PopupWindow id="popupWindowinPopup" width="200" height="200" selectedItems="{selectedItems}" dataProvider="{dataProvider}"/>    </s:BorderContainer>

If you use parsley, it is also worth noting that Context's get lost if you are binding using a PopupAnchor. But that's another blog post for another day.

Incidentally, take note that I don't use hostComponent. for my gestures, nor for data providers et al. But I shall be writing a blog post on the advantages and disadvantages of my approach used here in enterprise apps, but by all means, for smaller projects that don't have high degrees of complexity, it is perfectly acceptable to use hostComponent.runMyFunction() or dataProvider="{hostComponent.myDataProvider}" when using skins. MXML is duct tape, and in some cases of simple plumbing, duct tape is cost efficient.