3

假设我有一个 Flex 3 mxml 组件,称之为 A。A 有一个名为“b”的 get/set 属性。在 AI 中还有另一个内部组件 C,它是使用 mxml 指定的。在 mxml 中“实例化”组件 A 时,我可以在声明时指定 b 的值,一切正常。但是,当我使用 Actionscript 初始化组件时,我必须先将组件添加到呈现的容器中,然后才能设置所述组件的属性(在本例中为“b”)。当属性“b”的设置器以某种方式访问​​ A 中的 C 时,就会发生这种情况。

所以,这在运行时失败(它说 C 是空的)......

var a:A = new A();
a.b = "woopy"; //Sets the Label (declared in mxml) withn A to "woopy"
this.addChild(a);

另一方面,以下任何一项都可以

<customNamespace:A b="woopy"/>

或者

var a:A = new A();
this.addChild(a);
a.b = "woopy"; //Sets the Label (declared in mxml) withn A to "woopy"

如图所示,在将组件添加到容器后设置属性时,不会引发运行时错误消息。好的,这是有道理的,我想在组件被添加到容器之前,组件的内部结构并没有真正创建。不过,这有点烦人。有什么方法可以保证组件内部完全呈现而不将其添加到容器中?我不喜欢使用 actionscript 与 mxml 时的不同感觉。我想要一个解决方案,以便基本上在没有属性“参数”的 mxml 中声明 A 等同于在 AS 中使用 new 运算符声明 A。至少,就 A 的内部状态而言。

4

3 回答 3

8

要强制控件创建其子控件,您必须调用初始化方法。

即这应该工作:

var a:A = new A();
a.initialize();
a.b = "woopy";
this.addChild(a);

但是,到目前为止,我在声明 mxml 控件时所做的是将内部控件绑定到脚本块中声明的公共变量。例如

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            [Bindable]
            public var labelText:String = "[Default]";
        ]]>
    </mx:Script>
    <mx:Label text="{labelText}"/>
</mx:Canvas>

这样您就可以设置参数,而不必担心控件是否已创建。

于 2009-01-08T11:46:45.160 回答
4

没错——如果 B 的 setter 作用于 C,你就会遇到问题,因为当 A 被构造时,C 肯定还不存在,即使你已经在 A 的 MXML 中声明了 C。

关于是否有任何方法可以保证组件及其子组件在不将其添加到容器的情况下完全呈现和可用,答案是否定的——框架不会在组件上执行其创建和呈现魔法,直到它以某种方式添加到通过 MXML 或 addChild() 显示列表。

当然,您可以使用 visible 或 includeInLayout 属性(例如,在您的 A 组件上将两者都设置为 false)来绕过实际显示组件,或者如果您必须在脚本中进行实例化,您可以监听 A 的初始化或 creationComplete 事件(这两个事件都表明 A 的孩子已创建并准备好执行操作),然后等待设置 B 直到您收到该通知。不过,作为一般规则,我不建议直接调用 initialize() 方法。它是一个框架方法,无论如何都会在 addChild() 之后自动调用,通常最好让框架做它的事情,而不是绕过它。

var a:A = new A();
a.addEventListener(FlexEvent.INITIALIZE, a_initialize);
addChild(a);

private function a_initialize(event:FlexEvent):void
{
    a.b = "woopy";
    // ... and so on
}

如果你想确定的话,就这样吧。

Flex 团队的工程师 Deepa Subramaniam 最近在她的网站上发布了一段精彩的视频,详细介绍了 Flex 组件模型的详细步骤;我参加了她录制的 MAX 演讲,这很容易成为会议中最好的之一。值得一看(再看一遍,然后再看一遍),因为它的细节和全面性。她实际上在谈话中多次回答你的问题。这是很棒的东西。

祝你好运!

于 2009-01-08T13:10:03.013 回答
2

要回答您的主要问题,不,如果您想设置其属性,则不必将 AS3 实例化组件添加到显示列表中。在 MXML 中创建它与在 AS3 中创建它之间没有区别......当然,除非该组件没有正确构建。

Adobe(前身为 Macromedia)的 Flex 团队花了多年时间对 Flex 组件架构进行优化。该设计的两个重要部分与您的问题相关:

  1. 他们设计了失效和验证系统,以便您可以一次设置许多属性,但是在您完成所有更改之前,更改的效果不会发生。

  2. 首次实例化组件时,不会立即创建它的子组件。有一个最佳时间来做这件事,那就是在组件被添加到显示列表之后。

基本上,当 MXML 实例化组件和 AS3 实例化组件之间的行为存在差异时,这是因为构建组件时没有考虑这两个特性。

行为不正常的组件可能会执行以下操作:

private var label:Label;

public function get b():String
{
    return this.label.text;
}

public function set b(value:String):void
{
    this.label.text = value;
}

问题是组件开发者没有考虑到Label子组件可能还没有创建!最佳实践是将值保存在一个变量中,并在稍后将其传递给子组件无效(验证周期直到组件初始化并创建子组件后才会发生)。

private var label:Label;

private var _b:String;

public function get b():String
{
    return this._b;
}

public function set b(value:String):void
{
    this._b = value;
    this.invalidateProperties();
}

override protected function commitProperties():void
{
    super.commitProperties();
    this.label.text = this._b;
}

或者,如果您构建一个 MXML 组件,您可以执行类似的操作,但使用绑定而不是验证系统通常更容易:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Label text="{this.b}"/>
    <mx:Script><![CDATA[

    private var _b:String;

    [Bindable]
    public function get b():String
    {
        return this._b;
    }

    public function set b(value:String):void
    {
        this._b = value;
    }

    ]]></mx:Script>
</mx:Application>
于 2009-01-09T00:16:21.433 回答