Wednesday, December 12, 2007
A simple(r) workaround for the "Multiple sets of visual children" runtime error
One limitation (or feature?) of Flex 2/3 is that you cannot extend custom MXML components in MXML if you want to add additional chld elements in the subclass, e.g. if you have a MXML component BaseBox deriving from VBox which includes a Label (all written in MXML) and have a subclass of BaseBox called BaseBoxWithButtn also built in MXML you cannot just put new MXML child elements into this second MXML subclass. The code compiles fine but at runtime you'll get an "Error: Multiple sets of visual children have been specified for this component (base component definition and derived component definition)." error
This has been discussed several times now (here and here, also take a look at Ely's comment) and Peter Ent did a great job in describing a technique called "Templating" which is a way to tackle this.
However, sometimes you don't want to take the extra effort of using templates, especially not if you don't have the time to do so - exactly the situation we were confronted with last week. One option was to remove the MXML base classes and rebuild everything in ActionScript (again: too time consuming) - and the second one was to "fix" the framework and make it allow sublcassing and adding child elements through MXML. So we debugged the framework and found the corresponding line in the LayoutContainer class that that throws the mentioned runtime error.
In the end we created a subclass of the framework's LayoutContainer class and overrode the corresponding method. To use it, simply have your MXML components extend this class and specify the layout to use (absolute, horizontal, vertical). Beware, this worked just fine for our needs but it may not work for you at all. Also, it will probably also need some hacking to make this work with more complex / composite Container classes like Form, Panel etc. so don't complain :)
Demo (view Source enabled) available here. You can download the class here.
Dirk.
Comments
The easiest way, in my opinion, is to use view states. See this comment: http://www.munkiihouse.com/?p=37#comment-28
Posted By Borek / Posted At 12/12/07 9:41 AM
Borek,
I'm very interested in your viewstate suggestion. Do you have an example of this?
Thx a lot, Tom
Posted By Tom Van den Eynde / Posted At 12/17/07 11:32 AM
Not complaining, but if you use the following in your example you still get the error:
<local:ButtonBox width="200" height="100" backgroundColor="#EFEFFE"> <mx:Text text="Hey this is the last one!"/> </local:ButtonBox>
Posted By Will / Posted At 3/16/10 2:54 PM
A workable solution for multiple inheritance is to do what Adobe suggestes (until Flex 4 is released):
<?xml version="1.0" encoding="utf-8"?> <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" initialize="init()"> <mx:Metadata> [DefaultProperty("mxmlContent")] </mx:Metadata> <mx:Script> <![CDATA[ import mx.containers.HBox; import mx.core.UIComponent; [InstanceType("mx.controls.Label")] public var mxmlContent:IDeferredInstance;
// Define an Array of deferred properties // for a row of components. // Restrict the type of the component // to mx.controls.Button. [InstanceType("mx.controls.Button")] [ArrayElementType("mx.core.IDeferredInstance")] public var mxmlContentArray:Array; private function init():void { if (mxmlContent) { addChild(UIComponent(mxmlContent.getInstance())); } if (mxmlContentArray) { var controlHBox:HBox = new HBox(); for (var i:int = 0; i < mxmlContentArray.length; i++) controlHBox.addChild(UIComponent(mxmlContentArray[i].getInstance())); addChild(controlHBox); } } ]]> </mx:Script> <!-- DO NOT ADD CHILDREN HERE... ADD IN SUB-COMPONENTS --> </mx:VBox>
Posted By Will / Posted At 3/16/10 3:16 PM