Sunday, March 1, 2009

Flex Custom Components in Design Mode

I'm just learning Flex, but I already love it. Flex Builder/Eclipse is a descent development environment. The Flex Framework is very nice. The component and event model is pretty solid. It allows you to get started very quickly using MXML and then go deep using AS3. The integration between these modes is really well done. AS3 is a nice language - very easy for Javans and Sharpies to learn. The biggest benefit, however, is that I can draw anything I want and it will look and act the same in just about any browser. And I mean draw - not just layout. I can draw to a cross-platform graphics canvas in the browser. Cool.



Yesterday I started experimenting with custom components. The Flex documentation includes a good explanation of creating custom components using MXML and AS3 (download the pdf at http://livedocs.adobe.com/flex/3/createcomps_flex3.pdf, or view the live docs at http://livedocs.adobe.com/flex/3/html/help.html?content=Part4_CreateComps_1.html). For my purposes, I just needed a simple text object with rounded corners. It didn't seem like the TextArea control allowed rounded corners, so I subclassed VBox and included a TextArea control within it. I started first by creating a example of what I wanted using MXML. I then moved to AS3 and recreated the component. In both cases I found out something that many other folks already know; custom components generally don't draw themselves correctly in design mode. Frak. That's annoying.


When I deployed my swf, it looked correct. But inside Flex Builder's design mode, the controls were essentially blank. I tried any number of programming hacks to attempt to force the component to draw the TextArea, but all failed. An internet search found a number of posts on the subject. The consensus was that design mode is just frak'd and will not work with custom components. There was one dissenting opinion - a single line in a reply to one of those bleak posts suggested moving the component to an SWC file. That poster turned out to be on to something. Putting the component into an SWC yields another really nice bonus, which you will find out about near the end of this post (don't skip to the frak'n end!)


It's true. Components draw themselves correctly in Flex Builder design mode when they are first be compiled into an SWC file, then included as a library in the target project. Here is a quick summary on how I moved my components into an SWC file.


Using Flex Builder, I created a library project using the File/New/Flex Library Project menu item. I had pre-existing components that I wanted to put into the SWC file, so I first duplicated the package structure of the original project (the folder structure within the src folder) in the library project. I then moved the component source files to the new library project by selecting the component source files in the Flex Navigator tab and using the File/Move... menu item. Once I had my packages and source setup in the library project, I used the Project/Build Project menu item to build the SWC file.


Now I had my components in an SWC file, but I needed my target project to use it. I handled this in the target project's properties dialog. The project properties dialog is opened by selecting the project in the Flex Navigator tab, then selecting the Project/Properties... menu item (or right click on the project in Flex Navigator tab and select Properties from the contextual menu). Once the project properties dialog is open, the Flex Build Path can be selected in the list on the left. That shows the Flex Build Path panel. Selecting the Library Path tab shows the list of included libraries.







Project properties showing library path


It is possible, using the Add SWC... button, to add the SWC file directly to the target project. That would work. However, I think the best way to use your own components is to add the component library project itself to the target projects. The advantage that this approach has is that the SWC file will automatically be built by the projects that use it. This allows you to make modifications to a component and have those modifications automagically reflected in the projects that include it. The resulting work flow is natural and similar to having the component source directly in the target project. The big bonus is that the components can now be easily re-used in any number of projects.


So, I added the component library project to the target project by selecting the Add Project... button in the target project's Library Path properties, then I selected the component library in the resulting file dialog. That process effectively made the component project a library to the target project. Building the project shows how this arrangement works. When I make any modification to the components' source in the component library project and go back to the target project and choose the Project/Build Project menu item, I can observe the build process in the window that appears. The component library project is built before the target project is built.


Now when I look at the target application in Design Mode, the component draws itself correctly (mostly). That, of course, was the original purpose of this exercise. It still turns out that design mode is not perfect when showing custom components. It seems like it may keep a cache of the components, so some of your changes may not be reflected immediately. A workaround is to close and open the target project. That seems to synchronize with the latest version of the components. So, not perfect. Custom components in design mode still needs a little work. If I figure anything else out, I'll let you know. However, I think you will find that creating your own components in a library project offers other important benefits, so it should become your standard method for integrating your custom components into a project.