вторник, января 29, 2008

Тег [Bindable]. Flex

Если нам необходимо связать два свойства так, чтобы при изменении первого (источника) Flex автоматически копировал данные во второе (приемник), мы можем использовать метатег [Bindable]. Указав [Bindable] перед свойством-источником, мы даем понять Flex, что при изменении свойства надо генерировать событие.
[Bindable]
[Bindable(event="eventname")]

Если событие не указано явно, то по умолчанию создается событие propertyChange.

Тег [Bindable] используется в трех случаях:

  1. Перед определением класса. В этом случае, для всех (только!) public свойств (и переменных, и установщиков) Flex автоматически генерирует событие propertyChange типа PropertyChangeEvent, и все эти свойства могут использоваться как источники связывания данных.
    [Bindable]
    public class TextAreaFontControl extends TextArea {}
  2. Перед public, protected, private свойствами, определенными как переменные.
    При изменении свойств, Flex автоматически генерирует событие propertyChange типа PropertyChangeEvent. Можно также указать другое имя события.
    [Bindable(event="fooChanged")]
    public var foo:String;
  3. Перед public, protected, private свойствами, определенными как установщики. Для того, чтобы свойство являлось источником связывания данных, необходимо чтобы были определены и set, и get методы.
    Если указан только set метод, создается свойство "только для записи" и его нельзя использовать в качестве источника связывания данных.
    Если же указан только get метод, создается свойство "только для чтения", которое можно использовать как источник связывания данных и без вставки метатега - аналогично переменным, определеных как const.
    При изменении свойств, Flex автоматически генерирует событие propertyChange типа PropertyChangeEvent. Можно также указать другое имя события.
    [Bindable]
    public function set shortNames(val:Boolean):void { ... }
    public function get shortNames():Boolean { ... }

ВАЖНО: Если после изменения, свойство не приобрело новое значение, Flex не генерирует событие. Проверка осуществляется по принципу (oldValue !== value).

ВАЖНО: Если свойство содержит ссылку на объект и эта ссылка заменяется на другую, то событие генерируется. Если же изменяется объект, на который ссылается свойство, событие генерироваться не будет.

Свойства read-only и static по-умолчанию являются источниками связывания данных.

В этом примере установщик обновляет значение свойства, и вызывает событие для обновления связываемых свойств.

// Define private variable.
private var _maxFontSize:Number = 15;

[Bindable(event="maxFontSizeChanged")]
// Define public getter method.
public function get maxFontSize():Number {
return _maxFontSize;
}

// Define public setter method.
public function set maxFontSize(value:Number):void {
if (value <= 30) {
_maxFontSize = value;
} else _maxFontSize = 30;
// Create event object.
var eventObj:Event = new Event("maxFontSizeChanged");
dispatchEvent(eventObj);
}

Работа с цепочками связываемых свойств.

Следующий пример показывает довольно длинную цепочку связываемых свойств:
<mx:Text id="myText" text="{user.name.firstName.text}"/>

Для того, чтобы механизм связывания данных поймал изменения свойства text, достаточно сделать свойство text связываемым. Однако, если необходимо присвоить новое значение какому-либо элементу цепочки на этапе выполнения, то все они должны быть связываемыми. С другой стороны, изменение любого из элементов приведет к неспособности механизма связывания отслеживать изменение свойства text.
В случае работы с такими цепочками, можно пользоваться методами BindingUtils.bindProperty() или BindingUtils.bindSetter() .
Пример использования bindProperty():
bindProperty(myText, 'text', user, ["name","firstName","text"]);

Здесь представлена цепочка в виде ["name","firstName","text"] относительно объекта user. При этом user не является элементом цепочки.
В MXML цепочка связываемых свойств всегда задается относительно this:
bindProperty(myText, 'text', this, ["user", "name","firstName","text"]);

3 комментария:

Constantiner комментирует...

Поправлю в третьем пункте. Если для одних лишь только set-методов это утверждение верное, то биндинг для одних лишь get-методов вполне реальная и используемая вещь. Например, мы делаем некоторое свойство для чтения данных, которое берет комбинацию полей текущей выбранной строки DataGrid'а (ну пускай простая конкатенация). И мы можем сделать биндинг на это get-свойство DataGrid'а на событие "change" этого грида. Типа того :) И очень-очень часто это бывает полезным.

Racer комментирует...

Да, вот насчет get-методов как раз я и понял, что биндинг на них назначается по-умолчанию, без объявления тега [Bindable]. То же самое говорилось про const и static.
К примеру - есть свойство-getтер, возвращающее сумму каких-то двух переменных. Я не понял, как может Flex просечь, что нужно вызывать событие на связывание изменения этого свойства, если одна из переменных изменилась. Это событие можно задиспатчить только "ручками".

Честно скажу - пока не попробовал - не пойму :).

Constantiner комментирует...

Да, вот насчет get-методов как раз я и понял, что биндинг на них назначается по-умолчанию, без объявления тега [Bindable].
С чего бы? :)
Биндинг, по сути, это более удобная запись тех же листенеров событий. Генерится событие, ловится в тех местах, где должны быть изменения (подписание на биндинг). То есть если мы на get-метод ставим тег Bindable с указанием некоторого события, то генератор AS-кода (компилятор mxmlc) будет знать, что на это событие надо тупо извещать всех, кто подписан на это get-свойство, чтобы они изменили что-то там. Ну а где и как будет генериться это событие - всем по большому счету пофиг :)
На самом деле биндинг можно вешать и на функции. Читай подробнее тут.