如何在自定义的Flex 4组件中放置MXML子节点?

5
以下是一个自定义组件的例子。这只是一个带有标题标签和关闭图像(X)的盒子:
<?xml version="1.0"?>
<mx:Canvas ... >
    <s:VGroup>
        <s:Label text="(HEADING TEXT)" ... />

        (INSTANCE MXML)

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

当在MXML文档中使用该组件时,我希望将“(HEADING TEXT)”替换为一个参数(应该很容易实现),同时将“(INSTANCE MXML)”替换为多个标签、文本输入框、复选框等(可能更难)。
我找到了这个基于脚本的方法,但如果有编译时解决方案的话,我想要一个更加简洁的。有什么建议吗?
2个回答

10

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>

这将使您可以这样传递headingText:

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

你可以采用相同的方法传递其他简单值;只需声明一个公共属性,使其可绑定,然后在组件内部使用数据绑定将属性连接到其目标(或目标)即可。

对于复杂属性(比如你的INSTANCE MXML),也可以采用相同的方式实现。当你使用它时,它看起来像这样:

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

如果您想了解如何实现此功能,可以查看Flex框架中spark.components.Group组件的 mxmlContent 属性。由于源代码过长,无法在此处发布,我也找不到直接链接到源代码的在线链接。但基本思路如下(您可以在mxml文件的<fx:Script>块中执行以下所有操作 - 您不必创建一个纯AS类来执行此操作):

[1] 将属性声明为类型Array,并使用元数据ArrayElementType指定您希望数组包含的类型。

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

[2] 您需要一些逻辑来在运行时循环遍历数组,并将数组的内容添加到组件的显示列表中。覆盖 createChildren 方法是一个不错的触发地点。以下代码松散地源自 spark GroupsetMXMLContent() 方法实现,它并不涵盖所有可能的情况,但可以让您入门:

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);
    }
}

现在你的组件将拥有一个名为mxmlContent的属性,可以使用以下语法从父级mxml组件中设置:

<my:MyComponent>
   <my:mxmlContent>
       ... (MXML ELEMENTS HERE) ...
   </my:mxmlContent>
</my:MyComponent> 
你可以通过应用元数据[DefaultProperty("mxmlContent")]将你的新属性变成组件的default property。要从mxml中实现这个目标,只需要将元数据定义包装在<fx:Metadata>元素中即可。点击此处查看fx:Metadata示例。
将上述内容整合起来,你会得到可以像下面这样使用的东西:
<my:MyComponent headingText="Custom Text Here">
   (CUSTOM MXML CONTENT HERE)
</my:MyComponent>

编辑: 我应该在这里做一些注释:

  1. "Halo" 组件 (例如 mx:Canvas) 不支持上述使用方式的 addElement(),所以你可能需要使用 addChild()

  2. 你应该(可能)使用 Spark 组件而不是 Halo 组件。也就是说,使用 <s:Group> 作为基础组件,而不是 <mx:Canvas>。如果你这样做,那么你的组件将继承上面描述的 mxmlContent 属性。如果你想让你的组件拥有自己的 "content" 属性(甚至多个 "content" 属性),只需将其命名为不同的名称即可。


好吧,这不完全是一个编译时的解决方案,但也许不能避免运行时处理。你当然值得感谢,由于没有人更接近,我会接受这个答案。 - W3Coder
@W3Coder:感谢您的接受。我知道您想要一个编译时解决方案,但除非自己构建代码生成器,否则就没有其他方法可以实现。不过,值得一提的是:这种方法与 flex 框架处理子内容的方法相同,因此,以这种方式编写的组件应该与任何编译时解决方案一样高效。祝你好运!如果您找到任何不同/更好的解决方案,请回来告诉我们。 - Lee

7

这太棒了。感谢您提供这些信息。

由于我不需要对其进行太多控制,所以我稍微简化了这个解决方案。

我们容器MyComponent.mxml的代码:

<s:Group ... >
<fx:Script>
    <![CDATA[

    [Bindable]
    [ArrayElementType("mx.core.IVisualElement")]
    public var content:Array;

    ]]>
</fx:Script>

<s:Group width="100%" height="100%" mxmlContent="{content}" />

用法:

<myComponents:MyComponent
    xmlns:myComponents="myComponents.*"
>

    <myComponents:content>
        <s:Label />
        <s:Label />
        <AnythingWeWant...
    </myComponents:content>

</myComponents:MyComponent>

希望这能帮到任何人。 干杯。

1
很棒的帖子。正是我所需要的!Kamil,感谢您发布您的代码...这样更简单。 - Ofir
2
如果您在MyComponent.mxml中添加<fx:Metadata>[DefaultProperty("content")]</fx:Metadata>,则在使用该类时可以删除<myComponents:content>标签。请参见上文。 - Stephenr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接