- <?xml version="1.0" encoding="utf-8"?>
- <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" minWidth="1024" minHeight="768" xmlns:local="*">
- <mx:Script>
- <![CDATA[
- import mx.printing.FlexPrintJob;
- import mx.printing.FlexPrintJobScaleType;
-
- [Bindable]
- private var testdata:Object = {label: {innerLabel: "hello inner"}};
- protected function button1_clickHandler(event:MouseEvent):void
- {
- tb.label.innerLabel = "hello " + getTimer();
- display.text = testdata.label.innerLabel;
- }
- ]]>
- </mx:Script>
- <mx:Label text="{tb.label.innerLabel}" />
- <mx:Label id="display" text="{testdata.label.innerLabel}" />
- <mx:Button label="change" click="button1_clickHandler(event)" />
- <local:TBinding id="tb" source="{testdata}" />
- </mx:Application>
这里testdata是动态类型,但是通过TBinding 这个代理就可以达到Bindable的目的,testdata的设置和获得都通过这个代理得到。
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.utils.Dictionary;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
import mx.events.PropertyChangeEvent;
import mx.utils.ObjectUtil;
import mx.utils.UIDUtil;
import mx.utils.object_proxy;
[Bindable("propertyChange")]
public dynamic class TBinding extends Proxy implements IEventDispatcher
{
private var _source:Object;
private static var datas:Dictionary = new Dictionary();
private var bindProperites:Object = {};
private var _top:Boolean = true;
private var dispatcher:EventDispatcher;
public function TBinding()
{
dispatcher = new EventDispatcher(this);
}
override flash_proxy function getProperty(name:*):*
{
var result:* = source[name];
if (result && !ObjectUtil.isSimple(result))
{
if(!bindProperites[name])
{
var proBinding:TBinding = new TBinding();
proBinding.top = false;
proBinding.source = result;
bindProperites[name] = proBinding;
proBinding.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onPropertyChanged);
}
result = bindProperites[name];
}
return result;
}
private function onPropertyChanged(event:PropertyChangeEvent):void
{
var binding:Object = event.currentTarget;
var property:String = null;
for (var name:String in bindProperites)
{
if(bindProperites[name] == binding)
{
property = name;
break;
}
}
if(property == null)
{
return;
}
if (dispatcher.hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE))
{
var event:PropertyChangeEvent =
PropertyChangeEvent.createUpdateEvent(
this, property, source[property], source[property]);
dispatcher.dispatchEvent(event);
}
}
override flash_proxy function setProperty(name:*, value:*):void
{
var oldVal:* = source[name];
if (oldVal !== value)
{
// Update item.
source[name] = value;
// Notify anyone interested.
if (dispatcher.hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE))
{
if (name is QName)
name = QName(name).localName;
var event:PropertyChangeEvent =
PropertyChangeEvent.createUpdateEvent(
this, name.toString(), oldVal, value);
dispatcher.dispatchEvent(event);
}
}
}
private function construct():void
{
if(!source || !top)
{
return;
}
var uid:String = UIDUtil.getUID(source);
if(!datas[uid])
{
datas[uid] = this;
}
}
public static function bind(value:Object):Object
{
var uid:String = UIDUtil.getUID(value);
if(!datas[uid])
{
datas[uid] = new TBinding();
datas[uid].source = value;
}
return datas[uid];
}
public static function clear(value:Object):void
{
}
public function get source():Object
{
return _source;
}
public function set source(value:Object):void
{
if(source != value)
{
_source = value;
construct();
}
}
protected function get top():Boolean
{
return _top;
}
protected function set top(value:Boolean):void
{
_top = value;
}
object_proxy function get object():Object
{
return source;
}
public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
{
dispatcher.removeEventListener(type, listener, useCapture);
}
public function dispatchEvent(event:Event):Boolean
{
return dispatcher.dispatchEvent(event);
}
public function hasEventListener(type:String):Boolean
{
return dispatcher.hasEventListener(type);
}
public function willTrigger(type:String):Boolean
{
return dispatcher.willTrigger(type);
}
}
}
Recipe 14.9. Bind to Properties on a Dynamic Class
Recipe 14.9.1. Problem
You want to bind properties on a destination object to properties not explicitly defined on a dynamic class.
Recipe 14.9.2. Solution
Create a subclass of mx.utils.Proxy that implements the mx.events.IEventDspatcher interface and dispatch a propertyChange event within the setProperty override of the flash_proxy namespace.
Recipe 14.9.3. Discussion
The Proxy class lets you access and modify properties by using dot notation. To effectively work with dynamic property references, override the getProperty and setProperty methods of the flash_proxy namespace within your subclass implementation. With custom behaviors defined within these methods, you gain access to properties as if they were exposed directly on that class. However, dynamic property references are not enough to establish binding because data binding is an event-based system.
Because bindings are triggered by events, to create a Proxy class that is eligible for data binding, you must also implement the IEventDispatcher interface and its methods. In order for dynamic property references to be made for binding, the class is declared by using the dynamic keyword and defined by using the [Bindable] metadata tag with the event attribute set as propertyChange:
Code View:
[Bindable(event="propertyChange")]
dynamic public class Properties extends Proxy implements IEventDispatcher {}
An excellent example of when you would want to create a custom Proxy class is to access data loaded from an external source by establishing behavior rules within the setProperty and getProperty override methods, as opposed to writing a parser that will fill property values on a custom object from that loaded data.
For instance, an application loads the following XML from which element properties can be accessed and modified:
<properties>
<property id="name"><![CDATA[Tom Waits]]></property>
<property id="album"><![CDATA[Rain Dogs]]></property>
<property id="genre"><![CDATA[Rock]]></property>
</properties>
You can create a subclass of mx.utils.Proxy and use E4X in the setProperty and getProperty method overrides, allowing a client to access and modify XML data properties:
Code View:
override flash_proxy function getProperty( name:* ):*
{
return xml..property.(@id == String( name ) );
}
override flash_proxy function setProperty( name:*, value:* ):void
{
var index:Number = xml..property.(@id == String( name ) ).childIndex();
xml.replace( index, '<property id="' + name + '">' + value + '</property>' );
}
Data bindings are triggered by an event upon update to a property value. The setProperty override in this example, although it updates a property value, does not dispatch a notification of change. In order for binding to dynamic property references to be invoked, you must dispatch a PropertyChangeEvent from the Proxy subclass:
Code View:
override flash_proxy function setProperty( name:*, value:* ):void
{
var oldVal:String = xml..property.(@id == String( name ) );
var index:Number = xml..property.(@id == String( name ) ).childIndex();
xml.replace( index, '<property id="' + name + '">' + value + '</property>' );
var evt:Event = PropertyChangeEvent.createUpdateEvent( this, name,
oldVal, value );
dispatchEvent( evt );
}
The static createUpdateEvent method of the PropertyChangeEvent class returns an instance of a PropertyChangeEvent with the type property set to propertyChange, which is the default event for bindings and the one assigned in the [Bindable] metadata tag for the class.
The following example is a complete implementation of a Proxy subclass eligible for data binding:
Code View:
package com.oreilly.flexcookbook
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
import mx.events.PropertyChangeEvent;
[Event(name="complete", type="flash.events.Event")]
[Bindable(event="propertyChange")]
dynamic public class Properties extends Proxy
implements IEventDispatcher
{
private var _evtDispatcher:EventDispatcher;
private var _data:XML;
private var _loader:URLLoader;
public static const COMPLETE:String = "complete";
public function Properties()
{
_evtDispatcher = new EventDispatcher();
}
// load external xml.
public function loadProperties( fnm:String ):void
{
_loader = new URLLoader();
_loader.addEventListener( Event.COMPLETE, loadHandler );
_loader.load( new URLRequest( fnm ) );
}
// set data property and dispatch 'complete' notification.
private function loadHandler( evt:Event ):void
{
data = XML( _loader.data );
dispatchEvent( new Event( Properties.COMPLETE ) );
}
public function get data():XML
{
return _data;
}
public function set data( xml:XML ):void
{
_data = xml;
}
// use E4X to return property value held on xml.
override flash_proxy function getProperty( name:* ):*
{
if( _data == null ) return "";
else return _data..property.(@id == String( name ) );
}
// use E4X to modify property value on xml. Dispatch 'propertyChange'
override flash_proxy function setProperty( name:*, value:* ):void
{
var oldVal:String = _data..property.(@id == String( name ) );
var index:Number =
_data..property.(@id == String( name ) ).childIndex();
_data.replace( index, '<property id="' + name + '">' + value +
'</property>' );
var evt:Event = PropertyChangeEvent.createUpdateEvent( this, name, oldVal,
value );
dispatchEvent( evt );
}
// IEventDispatcher implementation.
public function addEventListener( type:String,
listener:Function,
useCapture:Boolean = false,
priority:int = 0,
useWeakReference:Boolean = false):void
{
_evtDispatcher.addEventListener( type, listener, useCapture,
priority, useWeakReference );
}
// IEventDispatcher implementation.
public function removeEventListener( type:String,
listener:Function,
useCapture:Boolean = false ):void
{
_evtDispatcher.removeEventListener( type, listener, useCapture );
}
// IEventDispatcher implementation.
public function dispatchEvent( evt:Event ):Boolean
{
return _evtDispatcher.dispatchEvent( evt );
}
// IEventDispatcher implementation.
public function hasEventListener( type:String ):Boolean
{
return _evtDispatcher.hasEventListener( type );
}
// IEventDispatcher implementation.
public function willTrigger( type:String ):Boolean
{
return _evtDispatcher.willTrigger( type );
}
}
}
flex 3 cook book 第14章