动态类型对象的Bindable

一般strict type的对象加入bindable很容易,只要在定义该对象的类上加上Bindable标签就行了,但是有时候我们并不想用strict type,但是又要用bindable怎么办呢?很多人都是把这个 动态对象换成了strict type。很得不偿失,有什么办法可以保持动态类型不变,也可以用Bindable呢? 我这里说的就是这种情况下Bindable的实现。首先看如下的 代码
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" minWidth="1024" minHeight="768" xmlns:local="*">
  3.     <mx:Script>
  4.         <![CDATA[
  5.             import mx.printing.FlexPrintJob;
  6.             import mx.printing.FlexPrintJobScaleType;
  7.            
  8.             [Bindable]
  9.             private var testdata:Object = {label: {innerLabel: "hello inner"}};

  10.             protected function button1_clickHandler(event:MouseEvent):void
  11.             {
  12.                 tb.label.innerLabel = "hello " + getTimer();
  13.                 display.text = testdata.label.innerLabel;
  14.             }
  15.         ]]>
  16.     </mx:Script>
  17.     <mx:Label text="{tb.label.innerLabel}" />
  18.     <mx:Label id="display" text="{testdata.label.innerLabel}" />
  19.     <mx:Button label="change"  click="button1_clickHandler(event)" />
  20.     <local:TBinding  id="tb" source="{testdata}" />
  21. </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章


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值