smulholland2
smulholland2

Reputation: 1163

Why won't visual elements display inside custom component extended from another custom component

I created a custom MXML component, TurboContent, that extends the NavigatorContent class:

<s:NavigatorContent xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx">

<fx:Metadata>
[DefaultProperty("mxmlContentFactory")]
</Fx:Metadata>

<s:Group id="midWrapper">
    <s:Button id="btnAdd" />
</s:Group>
<s:Group id="rightWrapper" >
    <s:DataGrid id="dgdSelect" >
        <s:columns>
            <s:ArrayList>
                <s:GridColumn headerText="Yo"></s:GridColumn>
            </s:ArrayList>
        </s:columns>
    </s:DataGrid>
    <s:Button id="btnRemove" />
    <s:Button id="btnClear" />
</s:Group>
</s:NavigatorContent>

I am trying to extend that custom component but when I add display elements to the second extended componet they elements are never seen. For instance: (The first custom component is in the comp package)

<comp:TurboContent xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx"
        xmlns:comp="comp.*">

<s:Button id="myButton"/>

</comp:TurboContent>

In this case, the 'myButton' component never shows up, but all the elements of the base component do (3 buttons and a datagrid).

Upvotes: 0

Views: 763

Answers (2)

merv
merv

Reputation: 76780

From what I can tell, the pattern of defining a component directly in MXML (as you referenced was done in the FIAW series) disallows the ability to then visually insert children in the container's display list. One of the problems is that the mxmlContent (which normally a skin would control) is statically defined in the component and it doesn't seem like one can use contentGroup inside the MXML component directly.

For better control, and what I consider a more strict implementation of the MVC pattern (which Flex 4, as a framework, implements), try separating your visual layout out into an MXML skin, and defining the component in AS3.

From what little I see of your component, I can't really make a judgment as to what properties of the component you want to expose to the container that will instantiate it. I'll at least give an example here of how one can pass info from the component to the skin.

I apologize for the MX components, but I only have Flex 4.1, and I wanted to make sure the program compiled fine. It shouldn't be too hard for you to swap in the spark versions.

Example Component (TurboContentAS.as)

package components {

  import mx.controls.DataGrid;
  import spark.components.NavigatorContent;

  public class TurboContentAS extends NavigatorContent {

    public function TurboContentAS() {
      super();
    }

    // Skin Parts that constitute the necessary parts of the component
    [SkinPart(required="true")]
    public var dgdSelect:DataGrid;  //just an example

    // property you want to expose to the instantiating object
    [Bindable]
    public var firstColumnHeader:String = "default header";

  }
}

Example Skin (TurboContentSkin.mxml)

<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/mx"
        alpha.disabled="0.5" >

    <fx:Metadata>[HostComponent("components.TurboContentAS")]</fx:Metadata>

    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
    </s:states>

    <!-- this is where the children that you add later go-->
    <s:Group id="contentGroup" left="0" right="0" top="100" bottom="0" minWidth="0" minHeight="0">
        <s:layout>
            <s:BasicLayout/>
        </s:layout>
    </s:Group>
    <s:Group id="midWrapper">
        <s:Button id="btnAdd" label="Add" />
    </s:Group>
    <s:Group id="rightWrapper" left="200">
        <s:layout>
            <s:VerticalLayout/>
        </s:layout>
        <mx:DataGrid id="dgdSelect">
            <mx:columns>
                <fx:Array>
      <!-- This will bind to the publicly exposed property in the component definition -->
                    <mx:DataGridColumn  headerText="{hostComponent.firstColumnHeader}"/>
                </fx:Array>
            </mx:columns>
        </mx:DataGrid>
        <s:Button id="btnRemove" label="Remove"/>
        <s:Button id="btnClear" label="Clear"/>
    </s:Group>
</s:Skin>

Example Instantiation

<components:TurboContentAS skinClass="skins.TurboContentSkin" firstColumnHeader="myHeader">
        <s:Button label="myButton"/>
    </components:TurboContentAS>

Upvotes: 1

Vladimir Tsvetkov
Vladimir Tsvetkov

Reputation: 3013

I'm curious to understand what you're trying to achieve?

From the pieces of code it seems you're trying to compose a new view which visually inherits the TurboContent-component and adds another button to it.

What happens really under the hood is that the NavigatorContent extends SkinnableContainer. SkinnableContainer has as a default property mxmlContentFactory, which once initialized can not be changed or substituted, or add stuff on top of it, unless you do that with ActionScript:

mxmlContentFactory = null; // or some IDeferredInstance

But then your approach of visual inheritance won't be visual inheritance, but content substitution.

The base class already has initialized it, so the subclasses can't do modifications to it.

Upvotes: 2

Related Questions