Flex 3 - Нужно ли добавлять компоненты перед установкой их атрибутов при использовании AS3? - PullRequest
3 голосов
/ 08 января 2009

Допустим, у меня есть компонент Flex 3 mxml, назовите его A. A имеет атрибут get / set с именем 'b'. Внутри A у меня есть другой внутренний компонент C, который указан с помощью mxml. При создании экземпляра компонента A в mxml я могу указать значение b при объявлении, и все работает нормально. Однако когда я инициализирую компонент с помощью Actionscript, я должен сначала добавить компонент в визуализированный контейнер, прежде чем я смогу установить атрибут (в данном случае 'b') указанного компонента. Это происходит, когда установщик для атрибута 'b' каким-то образом обращается к C внутри A.

Итак, это терпит неудачу во время выполнения (оно говорит, что 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. Мне нужно решение, чтобы в основном объявление A в mxml без атрибута «arguments» было эквивалентно объявлению A с использованием оператора new в AS. По крайней мере, с точки зрения внутреннего состояния А.

Ответы [ 3 ]

8 голосов
/ 08 января 2009

Чтобы заставить элемент управления создавать его дочерние элементы управления, вы должны вызвать метод initialize.

т.е. это должно работать:

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>

Таким образом, вы можете установить свои параметры, не беспокоясь о том, созданы ли элементы управления или нет.

4 голосов
/ 08 января 2009

Это верно - если сеттер B действует на C, у вас будут проблемы, потому что когда A сконструирован, C определенно еще не существует, даже если вы объявили C в MXML A.

О том, что существует какой-либо способ гарантировать, что компонент и его дочерние элементы будут полностью визуализированы и использованы без добавления его в контейнер, ответ - нет, фреймворк не будет выполнять создание и рендеринг магии для компонента, пока он каким-то образом добавлены в список отображения с помощью MXML или addChild ().

Конечно, вы могли бы использовать свойства visible или includeInLayout (например, установив оба в false для вашего компонента A), чтобы обойти фактическое отображение компонента, или если вам нужно было выполнить создание экземпляра в скрипте, вы могли бы прослушать либо события initialize или creationComplete A (оба из которых указывают, что дочерние элементы 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, недавно опубликовала на своем сайте превосходное видео , в котором подробно описывается модель компонентов Flex с детальной пошаговой детализацией; Я присутствовал на выступлении в MAX, где она записала его, оно было одним из лучших на конференции. Стоит посмотреть (и еще раз посмотреть, а потом еще раз посмотреть) на его детализацию и полноту. На самом деле она отвечает на ваш вопрос несколько раз во время разговора. Это классные вещи.

Удачи!

2 голосов
/ 09 января 2009

Чтобы ответить на ваш главный вопрос, нет, вам не нужно добавлять экземплярный экземпляр AS3 в список отображения, если вы хотите установить его свойства. Нет никакой разницы между созданием его в MXML и созданием его в AS3 ... если, конечно, компонент не был построен должным образом.

Команда Flex в Adobe (ранее Macromedia) провела много лет, дорабатывая оптимизации для архитектуры компонентов 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>
...