Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to put MXML child nodes within a custom Flex 4 component?

Here is an example of a custom component. It is just a box with a title label and a close image (X):

<?xml version="1.0"?>
<mx:Canvas ... >
    <s:VGroup>
        <s:Label text="(HEADING TEXT)" ... />

        (INSTANCE MXML)

    </s:VGroup>
    <mx:Image ... />
</mx:Canvas>

When using the component in an MXML document, I would like to have the "(HEADING TEXT)" replaced with a parameter (should be easy) as well as the "(INSTANCE MXML)" with several labels, text inputs, check boxes, etc. (maybe harder).

I have found this script-based method, but I would like a cleaner compile-time solution if one exists. Any suggestions?

like image 715
W3Coder Avatar asked Nov 27 '10 18:11

W3Coder


1 Answers

MyComponent.mxml:

<?xml version="1.0"?>
<mx:Canvas ... >
    <fx:Script>
        [Bindable]
        public var headingText:String = "Default Heading Text";

    </fx:Script>
    <s:VGroup>
        <s:Label text="{headingText}" ... />

        (INSTANCE MXML)

    </s:VGroup>
    <mx:Image ... />
</mx:Canvas>

That will let you pass in the headingText like this:

 <my:MyComponent headingText="Custom Heading Text" />

You can follow the same approach for other simple values you want to pass in; just declare a public property, make it bindable, then within your component, use data binding to hook the property to its destination (or destinations).

You can do the same thing for complex properties (like your INSTANCE MXML). It looks like this, when you use it:

<my:MyComponent>
  <my:thePropertyName>
     <s:Label text="whatever..." ... />
     <(OTHER MXML CONTENT) />
  </my:thePropertyName>
  <my:someOtherPropertyName>
    ....
  </my:someOtherPropertyName>
</my:MyComponent>

For an example of how to implement this, you can check out the mxmlContent property of the spark.components.Group component in the flex framework. The source is too lengthy to post here, and I can't seem to find an online link directly to the source; but the basic idea is this (you can do all the following inside a <fx:Script> block in an mxml file - you don't have to make a pure AS class in order to do this):

[1] declare the property as type Array, with metadata ArrayElementType to indicate the type you want the array to contain.

[ArrayElementType("mx.core.IVisualElement")]
public function set mxmlContent(value:Array):void {
    _mxmlContent = value;
}
private var _mxmlContent:Array;

[2] You'll need a little bit of logic to loop over the array at runtime, and add the array's contents to the component's display list. The createChildren override is a good place to trigger this. The following is derived loosely from the implementation of setMXMLContent() method of spark Group. It doesn't cover all possible cases, but it'll get you started:

override protected function createChildren():void {
    super.createChildren();
    if( _mxmlContent == null ) return;
    for (i = 0; i < _mxmlContent.length; i++) {   
        var elt:IVisualElement = _mxmlContent[i];
        addElement(elt);
    }
}

So now your component would have a property called mxmlContent, which you could set from a parent mxml component using the syntax:

<my:MyComponent>
   <my:mxmlContent>
       ... (MXML ELEMENTS HERE) ...
   </my:mxmlContent>
</my:MyComponent> 

You can make your new property into the default property of your component, by applying the metadata: [DefaultProperty("mxmlContent")] your component class. To do this from mxml, just wrap the metadata definition in an <fx:Metadata> element. see here for example of fx:Metadata.


put all the above together, and you'll get something you can use like this:

<my:MyComponent headingText="Custom Text Here">
   (CUSTOM MXML CONTENT HERE)
</my:MyComponent>

Edit: I should make a couple of notes here:

  1. "Halo" components (like mx:Canvas) don't support addElement() as used above, so you'll probably want to use addChild() instead.

  2. You should (probably) be using spark components instead of halo components. Meaning, use <s:Group> as your base instead of <mx:Canvas>. If you do this, then your component will inherit the mxmlContent property described above. if you want your component to have its own "content" property (or even multiple content properties), just name them something different.

like image 128
Lee Avatar answered Oct 14 '22 16:10

Lee