Flex UI Components That Aren’t UIComponents
In Flex there are UIComponents and there are UI ‘components’. The distinction is not obvious and for most developers most of the time it is not important. But if you happen to be creating a visual design environment for Flex (as we are) the distinction is vital..
In our next beta of Amethyst, our Flex IDE for Visual Studio, we will extend drag-and-drop design support for complex controls such as grids, data grids and navigators. At first sight, these may appear to be pretty much like all other controls - but they aren’t. The DataGrid, in particular, poses some special problems - not least because some of its user interface components aren’t user interface components. These curious components are the DataGridColumns that populate a DataGrid.
At first sight DataGridColumns look like ordinary user interface components; they are, after all, components which appear in a user interface, but their appearance is deceptive!
Let me explain. The vast majority of Flex visual components have a shared line of descent in the class hierarchy. A Button, for example, descends directly from the UIComponent class, a ComboBox descends from ComboBase which itself descends from UIComponent, a Tree descends from List which descends from ListBase which descends from ScrollControlBase which descends from UIComponent. Well, you get the picture. No matter which happens to be the immediate ancestor of a component, ultimately its family tree leads back to UIComponent.
This is all very useful and entirely what you would expect. The UIComponent class and its ancestors provide all the behaviour required by visual controls: x and y positions, width and height, id, parent and so on. In our drag and drop Designer we need all this information in order to create controls based on their MXML definitions in source code and to ‘wire them up’ to their parents - that is to the containers which contain them.
Even ‘complex’ control such as Grids stick to the rule that every visual component descends from UIComponent. This is true even for components that are ‘owned by’ other components such as the rows within a grid (GridRow descends from HBox, Box, Container and UIComponent) and the grid items which live inside grid rows (GridItem also descends from HBox, Box, Container and UIComponent).
DataGrids are not Grids!
At first sight, you might think that DataGrids and grids are essentially similar things and are likely to have similar lines of descent. As a matter of fact, they don’t. Grid descends from Box and Container while DataGrid descends from DataGridBase, ListBase and ScrollControlBase.
Even so, the DataGrid class itself is no problem since its ancestor, ScrollControlBase, is the immediate descendent of good old UIComponent (hurrah!). But the columns which a DataGrid contains certainly are a problem. The DataGridColumn class descends from CSSStyleDeclaration (eek! what?) which descends from EventDispatcher which descends from the base class, Object. If you think that’s a surprising inheritance tree for a visual component, just try to imagine how we felt!!! (ah, how I recall the handfuls of hair I tore out...)
Up to that point in the development of the Amethyst Designer we had naively worked on the assumption that all user interface components descended from UIComponent. Oh well, back to the drawing board...
As a consequence of its unusual line of descent, the DataGridColumn class lacks many properties which we had hitherto regarded as essential - for example, ‘parent’. Unlike other controls it cannot be added to a container. There are other properties missing too, which make it impossible to treat these objects like most others.
Now, I can only hazard a guess as to why DataGrid columns were given this rather eccentric line of descent. However, if it can happen once, it can happen again. Rather than do a special ‘fix’ to handle this one case, we decided that the Amethyst Designer really needed a generic way of handling visual components that do not derive from UIComponent. This involved, I may say, a pretty substantial rewrite of some of the core features of the Amethyst Designer. It meant that we not only had to find ways of creating non-UIComponent objects but we also had to have a means of interrogating them to find the correct way in which to add them to a visual design. In the case of DataGridColumn objects, they must be added to the array that populates the columns property of a DataGrid. This is a fundamentally different process from adding a component as a child of a container (which is how UIComponent objects operate).
Handling properties such as ‘columns’ provides its own interesting programming challenge. In most cases, Flex properties are defined in MXML as simple attributes placed into a single tag, like the x, y, width and height attributes here:
<mx:Button height="30" width="90" x="30" y="300"/>
But columns aren’t like that. They take the form of multiple objects nested between a pair of ‘columns’ tags like this:
Normally a tag or a pair of tags defines a component whose class name forms the first item in the tag. But here ’columns’ is a property name and when the Designer creates the visual representation of the MXML code it has to know that it is dealing with a property and not a class; it also has to know which component the property belongs to and, finally, it has to create the items (here the DataGridColumns) to assign to that property.
In fact, it is also possible (if not that common) to write simple properties using a double-tag syntax (and we have to handle those too) - for example, you could write x like this:
As though dealing with complex properties isn’t a tricky enough task, there is one more complication. Visual Studio has well-defined ways of handling properties such as arrays of objects. It provides property editors. First, you click a button alongside a property name in the Properties panel (here the ‘columns’ property)...
...and a dedicated editor pops up with its own Property page for each object in an array and also buttons to add, delete or move existing items:
And when the property editor pops down again, your DataGrid and its columns have been changed:
None of this happens ‘automatically’. To get this to work for Flex we’ve had to do it all the hard way. With luck, when we release the next Amethyst beta, all this should give the impression of being just as simple and straightforward as altering the properties of any simple UIComponent control like a button or Canvas.
But, take my word for it, it isn’t... ;-)