When I started using Flex 4 I tried to get up to speed by using the new version in a manner similar to its predecessor until I came across roadblocks or when I determined I was able to take advantage of the new features I had heard about such as skinning, states, etc. I quickly found that there were some obvious Flex questions that I was not able to answer without diving into the API docs and by playing with some examples so I could truly understand what is going on. I knew from past experience that Halo was hiding a lot of the dirty details about what was going on in the display list, but once you start using Spark, you will come to see just how much Halo was really doing under the covers and why Spark is a much more ‘honest’ way of working with the display list.
One of the first things I hit was the term ‘elements’. What is an element? Is it the same thing as a child? Initially I concluded that elements where just another way of saying children (wrong). I figured that all elements where child components and vice versa. Then I noticed that some of the methods that I was expecting to find inside the new containers (as well as some of the existing ones) were missing. Where is getElements()? How do I find out how many elements I have? Can I just use getChildren() and treat the result like an IVisualElement (which itself was a mystery to me at the time).
So I started to really read the API docs, Flex source and blog postings about these topics. I also tried to decipher the slides shows that some bloggers posted about the great new features of Flex 4, but it turns out the slides do not make much sense if you do not have the audio of the presentation to go along with them.
Finally, I started to put together some very basic demos that would help me explore the new world of elements and help me figure out what I was really doing when it came to using the new Spark containers.
I’ll start by stating the questions I had at the time. Question number one was “How do I get the all the elements of a spark container?” I was expecting a getElements() method similar to the familiar getChildren() method from Flex 3. Instead, what you have are two properties: numElements and numChildren. To obtain all of the elements of a container, you can use a for-loop with the numElements count and call the getElementAt() method for each one. Pretty obvious after you realize there is no getElements convenience method. Question number two was: “what is the difference between an element and a child?”. Let’s look at the difference between elements and children….
An element is basically anything that implements the IVisualElement interface. A child is anything that extends from DisplayObject. When trying to determine if a component is an element or a child or both, remember that a UIComponent is a descendant of DisplayObject, so all UIComponents are DisplayObjects and therefore can be children. Also know that UIComponent implements IVisualElement so all UIComponents are considered ‘elements’. However, this does not mean that all DisplayObjects are elements. A DisplayObject that is parented by a container (ie, contained within that container) is a child of that container. But it is only an element of that container if it also implements IVisualElement. So when is a DisplayObject a child of a container and when is a DisplayObject an element of that container? Some examples showing different types of containers will answer this question.
In the first example, we look at the result when the container in question is an older Halo container (in this case, a Halo Panel). Because Panel is a descendant of DisplayObjectContainer, it has the addChild() method. In addition, because it descends from Container (which implements IVisualElementContainer), it also has the addElement() method. We will see that the Container’s implementation of the IVisualElementContainer interface is just a facade to the display list API but it is implemented here so that it will have the same methods that we find in the newer Spark containers that also implement IVisualElementContainer.
So we can add both children and elements to this container. However, we cannot add just any type of element to it. There is a difference between VisualElements and GraphicElements. VisualElements implement the IVisualElement interface, but GraphicElements implement an extension of IVisualElement called IGraphicElement which has some extra features that the container needs to know about. Therefore, some elements (such as the GraphicElement) cannot be added directly to the Halo Panel. The compiler will complain that it needs to be wrapped in a Group container first. More on why in a second….
The Panel in the following example contains several UIComponents including another Halo Panel, a Spark Panel, some Halo Buttons, some Spark Buttons and a SkinnableContainer with child components of its own. (Note that the components inside the SkinnableContainer will NOT be children of the Panel. They are children of the SkinnableContainer.) All of these components are DisplayObjects so they are all ‘children’. Clicking the ‘Show Children” button reveals as much. In addition, all of these components are UIComponents (which implements IVisualElement) so they are all ‘elements’ as well.
Now take a look at the next example. This time the container in question is a Spark Group. Like the Halo Panel in the previous example, Group is a descendant of DisplayObjectContainer so it has the addChild() method and because it implements IVisualElementContainer, we can add IVisualElements (‘elements’) to it as well using addElement(). The Group container can also handle GraphicElements such as a Rect (a Rectangle GraphicElement) which the Halo Panel could not. This is because, unlike the Halo Panel, the Group object knows how to display GraphicElements in an optimized manner. What does that mean? Read on…
GraphicElements require a little more involvement from their parent containers than do regular VisualElements. The key here is the IGraphicElement interface that GraphicElements implement. As I mentioned before, it is an extension of the IVisualElement interface (which is why GraphicElements can be added to the Group through the Group’s addElement() method). But GraphicElements are not DisplayObjects so they cannot be seen unless they get ‘drawn’ onto another DisplayObject by their parent. So if you add a Rectangle to a Group, the Group will also need to have a DisplayObject to draw the Rectangle on so it will show up in that Group. Just to optimize things a bit, the Group can actually reuse the same DisplayObject to draw multiple GraphicElements when possible. The container can use any DisplayObject that implements ISharedDisplayObject to draw the GraphicElement. In the first example, the Halo Panel could not do this so it could not draw GraphicElements in this optimized manner. Thus, the compiler gave an error saying you have to wrap it in a container that can deal with this. The Group container can do this so GraphicElements can be added to a Group.
One other thing to note: while some GraphicElements will allow the Group parenting them to create a DisplayObject for that GraphicElement to be drawn in, others will create their own DisplayObjects to be drawn in, and this interface (IGraphicElement) allows the parent Group to manage that DisplayObject even though the parent Group did not create it.(ie to add the DisplayObject as a child so the IGraphicElement can be drawn into it).
So what does this mean? It means that in our next example, the number of children is not the same as the number of elements. This example uses the same set of components from the first example, but it adds 4 rectangles which are GraphicElements. The children are all IVisualElements. However, they are not all children. The rectangles are GraphicElements which do not descend from DisplayObject. BUT, the Group knows that it needs to add DisplayObjects so that the GraphicElements can be drawn inside the container. The rectangles are different sizes and rotations so the Group decided to add two new DisplayObjects to draw the four rectangles on. Pretty cool, huh?
Now look at example 3. Instead of a Group, we are using a SkinnableContainer. The SkinnableContainer has the same set of components as the previous example. But, the SkinnableContainer uses a Skin for its visual representation and that Skin is the first and only child of the SkinnableContainer. The SkinnableContainer’s default Skin class is comprised of a Rectangle (for the border) and a Group called the ContentGroup which all skins for containers need so Flex know where to add children to the container.
So this example illustrates the fact that, while the SkinnableContainer has 10 elements, it only has one child: the Skin of the SkinnableContainer. In turn, that skin has only one child: the ContentGroup Group component. You may be asking “why are there not 2 children of the Skin: one being the ContentGroup and the other being the DisplayObject for the Rectangle (serving as the border) to be drawn in? The answer is because the Skin class descends from the Group class and Groups will add DisplayObjects when necessary to draw any GraphicElements they contain. In this case, no new DisplayObjects were required. The Skin class just drew the Rect GraphicElement directly onto itself. It can do this because the Skin’s ancestor, the Group class, also implements ISharedDisplayObject which means that it can serve as a shared DisplayObject used to draw GraphicElements when necessary. So the Skin manages the DisplayObjects used to render the GraphicElement, and the in this case, the DisplayObject it is using to draw on is itself! If you added other Rectangles to a custom skin and assigned that to the SkinnableContainer, the Skin might decide it needed more DisplayObjects to draw those other Rectangles and you would therefore see more children in the Skin’s child list.
One other thing to note is that the number of elements stays the same when you look at the element list for the SkinnableContainer, its Skin, and the Skin’s ContentGroup. If you look at the source for SkinnableContainer, you will see that it determines the numElements value by getting the corresponding numElements value from the currentContentGroup so basically it is just re-routing you to its content group when you query for elements. Likewise, the Skin of the SkinnableContainer does something similar. It extends from Group, which gets the numElements property from its internal mxmlContent property which is an array of visual content children for this Group. Both of these are analogous to the RawChildren property of the Panel container which you used to get ALL the children of the Panel rather than just the children that you added to the Panel which came from the getChildren() method.
After reading all of this, it may not seem any clearer than when you started. You basically have to familiarize yourself with the inheritance hierarchy and relationship between 7 different classes/interfaces:
1. DisplayObject
2. UIComponent
3. Container,
4. IVisualElement,
5. IGraphicElement,
6. IVisualElementContainer
7. ISharedDisplayObject
Once you know who these fit together, you’ll soon understand the difference between elements and children. I’m sure I’ve messed up some of the facts and gotten a few things wrong so feel free to put corrections in the comments section if you think I’m way off base on any of this. 🙂
UPDATE: Check out these links for more information on this and related topics:
-
- Article on the Gumbo DOM Tree API (this is full of great detail on this topic)
- Nice example on the the difference between the owner and parent of a component. Very nice.
Good read. I was wondering the same thing today. Now I know 🙂
You might also find this post useful:
http://flexponential.com/2009/12/08/differences-between-ivisualelement-parent-and-ivisualelement-owner/
I thought I’d have a crack at summing this up for you. Ignore the Flex 3 stuff, they are just trying to make it compatible, lets concentrate on Flex 4.
Elements where introduced to handle FXG. FXG classes are not DisplayObjects and therefore cannot be added as children (addChild is built into the player and requires a DisplayObject).
Therefore instead of Adobe give you different methods for DisplayObjects and FXG they’ve hidden the old methods and provided a single set of new ones for elements.
When you add something via these methods the component looks to see if its a DisplayObject, if it is it adds it as a child, if not it it must be FXG and therefore it is handled in a different way (extra DisplayObjects are created internally to draw the FXG into, and if possible these are shared with multiple FXG objects).
So to sum up
– DisplayObjects are children & elements
– FXG are elements alone (secret DisplayObjects are created to display them)
@Tink
Absolutely fantastic summation! When I was trying to determine the reasons for the IVisualElement interface, it had not occurred to me that Adobe had done all of this just to facilitate the addition of the FXG elements alone, but it certainly makes sense after reading your explanation.
Excellent post, Bill!
When I was looking through this stuff I assumed that elements were similar to Flex 3’s rawChildren and children are IUIComponents that get laid out and measured.
I’m glad I read your post, else I may not have figured this out until it became painfully too late. Thanks.
Hmm .. but this doesn’t seem to work for the List component. There is no numElements and the numChildren seems off. I wrote a simple dataProvider and with 5 elements / attached it / overrode childrenCreated() … 1 child?? doesn’t seem right — I wish the docs / code was more clear here with Adobe — it really makes our job super difficult and time consuming.
@Brian Russel Davis – Along with children vs. elements, there is another distinction in spark containers which we think of as elements vs. items. An element is a visual thing like a Rect or a Button, whereas an item is a piece of data that has no visual representation, for example a String or Object. An element can be added to the display using addElement(), but an item can only be added to the display if it is first pushed into an ItemRenderer that is able to turn that data into a visual representation.
In spark, Group/SkinnableContainer are the base containers for dealing with elements and DataGroup/SkinnableDataContainer are the base containers for dealing with items (List extends from SkinnableDataContainer).
These data containers don’t have a concept of elements, but rather have a dataProvider that hold data items that are then turned into visuals using an ItemRenderer. You access the data items of a List/SkinnableDataContainer/DataGroup via the dataProvider property, for example: myList.dataProvider.length.
If you really want access to the item renderer instances that are created by the List you can get at them by using the IVisualElementContainer methods on the List’s dataGroup skin part, for example: myList.dataGroup.numElements.
List has virtual layout on by default so if you have a thousand data items in the List, but only a couple are actually visible at one time then the List will only create renderers for those in view and recycle them when possible. This might be why you are seeing a different number of children than you might expect.
In general you should only access or modify the data items of a List and not access the item renderer instances directly unless you know what you are doing and are aware of the consequences of virtual layout.
Here are some links for more information on DataGroup and virtual layout:
http://opensource.adobe.com/wiki/display/flexsdk/Spark+DataGroup
http://opensource.adobe.com/wiki/display/flexsdk/Spark+Virtualization
http://flexponential.com/2009/07/10/performance-implications-of-the-list-itemrenderer-vs-itemrendererfunction-properties/
Excellent Steven – thanks for pointing that out! 🙂
I would have thought Adobe would be making it easier to develop, and it appears it’s making it harder. Why? Simply documenting and explaining these things would do the job, but Adobe consistently sucks with documentation, samples, and explanations.
The new approach makes a lot of sense–more granular, clearer distinctions. But the absence of getElements and making developers code their own is almost criminal, in that 1) it’s absolutely not obvious and 2) Adobe once again sucks time out of our days where IT SHOULD JUST WORK.
Thanks for the post–if it weren’t for you I’d be stuck again. I’m not as good a coder and the extra insight really helps.
Thanks Charlie. I agree that they should have included the getElements method and I’m really at a loss to explain why it was left out. At times I’ve wondered why Adobe does things the way they do, but usually I find that I just did not realize their ultimate goal. For example, check out Tink’s comment where he lays out their underlying motive for using Elements. He makes some great points. But I do understand your frustration and I’ve been there myself. 🙂
Thanks for this article. I have hit this a few times and it’s fantastic to see the details so well laid out!
Thanks for this preciated information. I´m figthing with this problem, in my transition from pure Flash and as3, to flex 4 on fly (no books, only writing code xdxd), and it´s hard to work with components, because elements work difference like Child, and the constants errors are awful ;-).
Best regards from Spain!.
Pingback: Flex4 Elements VS Children | arctton
Ultimate explanation thanks for your effort and the way of explantion.
Man, this article really helped fill some gaps in my knowledge of Flex 4. Thank you muchly for taking the time to write it up.
@Tink, thanks for your input as well. I have not had the chance to work with FXG (in terms of the Adobe product suite) but that is still useful info.
真他妈的是一篇好文章!(This is a Fing great article!)
Great article!! Thanks Bill and @Tink for summing it up.
Gr8 Man!!! You did well!!!
Excellent article! Thanks Bill!
Great article…really helpful to understand this distinction.
Hi Bill! Your article is excelent. It help-me solve a big problem!
But in my case i would like to list all elements. However my TitleWindow
had many Containers, such TabNavigator, ViewStack, Canvas etc.
In this case i wrote this code, that work very well for me. I would like share too:
private function getAllComponents():void
{
var componentsRoot : Array = new Array();
var componentNames : Array = new Array();
componentsRoot.push(this);
componentNames.push(this);
while (componentsRoot.length >= 1 )
{
var components : Array = new Array();
for( var i : Number = 0; i< componentsRoot.length; i++ )
{
//percorrendo apenas elementos que são containers
//running only elements that are containers
if (componentsRoot[i] is IVisualElementContainer)
{
var component : IVisualElementContainer = (componentsRoot[i] as IVisualElementContainer);
//percorrendo todos os elementos do componente atual
//running all elements of actual component
for ( var j : Number = 0; j< component.numElements; j++ )
{
if ( ( component.getElementAt(j) as UIComponent) != null)
{
//armazenando todos os elementos para percorre-los caso sejam containers
//storing all elements for running if are containers
componentNames.push( component.getElementAt(j) as UIComponent );
components.push(component.getElementAt(j) as UIComponent);
}
}
}
}
//alterando os componentes a serem percorridos
//changing the components for running and find their elements
componentsRoot = components;
components = null;
}
for (var y : Number = 0; y<componentNames.length; y++ )
{
//apenas mostrando os dados em um txtArea
//just show the data in textArea Component
if (( componentNames[y] as UIComponent ).id != null)
txtArea.text = txtArea.text + ( componentNames[y] as UIComponent ).id + "n";
}
}
Thank you!
THANK YOU!!!
Hi Bill! Your article is look much better then Adobe doc for element and child. Tx
Thanks for this post.
Am new to flex. Its really useful for me. Clear explanation about Children and elements in flex 3 and 4.
I have one question.
I have two application and one common component. I tried to check one component in that common component. The checking component is there in only one application.
I tried to check that component by using FlexGlobals.topLevelApplication.groupName.getElementByName(“component name”)
While checking this, It return null object reference. I checked condition not null also. But, I did not get solution.
If any body know solution Please help me?
Really well put together,
thanks
glenn
tinylion development uk
Thanks a lot…it was an awesome explanation..u have cleared all my concepts about this in depth 🙂
I’ve been struggling with this problem for a week, you are my new hero!
nice explanation with keeping concept of basic things which some time people not focus. Its great. Thanks a lot . 🙂
Pingback: 深入Flex4--了解Element和Child的异同 - 网站前端 - 开发者第1666290个问答
Greate clarification, thanx very much! I can’t realize why did Adobe make this mess? Whether it is backward compatibility issues or what? Simplicity is not a goal?