2008年6月10日 星期二

Flex - 純手工設定 DataBinding 的方式

Ticore's Blog

Flex 的 DataBinding Expression 功能雖然非常方便
不用自行呼叫 addEventListener,省去了不少程式碼
但是換個角度看,Flex DataBinding 其實是有點缺乏彈性
不用自行加入監聽事件,同時也意味著不能移除監聽事件

然而,自行利用 addEventListener 方式實作的 DataBinding
感覺好像又沒有 Flex Compiler 產生的好~
於是想要觀察 Flex Compiler 產生的程式碼
進而自行模仿實作 DataBinding

想要觀察 Flex Compiler 產生的 ActionScript 很簡單
只要加入編譯參數 -keep-generated-actionscript=true
就可以在 /src/generated 下找到了

舉例來說,想要模擬以下的 DataBinding Expression:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   backgroundColor="#FFFFFF" layout="vertical" fontSize="12">
   
  <mx:Label text="Binding Source:" />
  <mx:HSlider id="slider1" snapInterval="1" maximum="100" value="50" />

  <mx:Label text="Binding Destination:" />
  <mx:HSlider id="slider2" snapInterval="1" maximum="100" value="{slider1.value}" />
  
</mx:Application>
<!-- Ticore's Blog - http://ticore.blogspot.com/ -->

步驟有點多~~

  1. 實作 mx.binding.IBindingClient; 介面

  2. 匯入必要的 Class:

    import mx.binding.*;
    import mx.binding.utils.*;
    import mx.core.mx_internal;
    

  3. 宣告 Bindings, Watchers 等屬性:

    mx_internal var _bindings : Array = [];
    mx_internal var _watchers : Array = [];
    mx_internal var _bindingsByDestination : Object = {};
    mx_internal var _bindingsBeginWithWord : Object = {};
    

  4. 建立 Bindings 與 Watchers:

    mx_internal::_bindings[0] = new Binding(this,
     function():* {return slider1.value;},
     function(sourceReturnValue:*):void {slider2.value = sourceReturnValue;},
     "slider2.value");
    
    mx_internal::_watchers[0] = new PropertyWatcher("slider1",
       {propertyChange: true},
       [mx_internal::_bindings[0]],
       function(propertyName:String):* { return this[propertyName]; });
    
    mx_internal::_watchers[1] = new PropertyWatcher("value",
       {valueCommit: true, change: true},
       [mx_internal::_bindings[0]], null);
    
    mx_internal::_watchers[0].updateParent(this);
    mx_internal::_watchers[0].addChild(mx_internal::_watchers[1]);
    
    mx_internal::_bindings[0].execute();
    

最後一個步驟看起來就有點複雜了
Binding 的功能有點類似 Event Handler,負責執行 DataBinding 運算
而 Watcher 則是類似 Event Listener,負責監聽資料來源的變化
為什麼不用標準的 Event Listener 機制
看 Flex Source 上寫的是因為效能考量

先看一下 mx.binding.Binding 類別的使用方式
public function Binding(document:Object, srcFunc:Function, destFunc:Function, destString:String)

  • document: binding 目標的文件
  • srcFunc: 用來取值的函式
  • destFunc: 將值指定到目的地的函式
  • destString: 用來告訴 ValidationManager 驗證該欄位

至於 Watcher 其實只是一個上層類別
實際使用時,需要視 Binding Source 種類決定使用哪一種子 Watcher
XMLWatcher, PropertyWatcher, StaticPropertyWatcher, RepeaterItemWatcher, RepeaterComponentWatcher, FunctionReturnWatcher, ArrayElementWatcher

Watcher 有一個最特別的地方是,它具有父子關係
上層的父 Watcher 會觸發下層的子 Watcher 物件
可以藉由以下 Watcher 函式設定:

public function updateParent(parent:Object):void;
public function addChild(child:Watcher):void;
public function removeChildren(startingIndex:int):void;
public function updateChildren():void;

綜合上述的步驟
完整手工設定的 DataBinding MXML 程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   backgroundColor="#FFFFFF" layout="vertical" fontSize="12"
   implements="mx.binding.IBindingClient"
   creationComplete="onCreateComplete();">
  
  <mx:Script>
   <![CDATA[
    import mx.binding.*;
    import mx.binding.utils.*;
    import mx.core.mx_internal;
    
    mx_internal var _bindings : Array = [];
    mx_internal var _watchers : Array = [];
    mx_internal var _bindingsByDestination : Object = {};
    mx_internal var _bindingsBeginWithWord : Object = {};
    
    public function onCreateComplete():void{
     
     mx_internal::_bindings[0] = new Binding(this,
      function():* {return slider1.value;},
      function(sourceReturnValue:*):void {slider2.value = sourceReturnValue;},
      "slider2.value");
     
     // mx_internal::_bindings[0].mx_internal::isEnabled = false;
     
     mx_internal::_watchers[0] = new PropertyWatcher("slider1",
        {propertyChange: true},
        [mx_internal::_bindings[0]],
        function(propertyName:String):* { return this[propertyName]; });
     
     mx_internal::_watchers[1] = new PropertyWatcher("value",
        {valueCommit: true, change: true},
        [mx_internal::_bindings[0]], null);
     
     mx_internal::_watchers[0].updateParent(this);
     mx_internal::_watchers[0].addChild(mx_internal::_watchers[1]);
     
     mx_internal::_bindings[0].execute();
     
     // BindingManager.debugBinding("slider2.value");
    }
   ]]>
  </mx:Script>
  
  <mx:Label text="Binding Source:" />
  <mx:HSlider id="slider1" snapInterval="1" maximum="100" value="50" />

  <mx:Label text="Binding Destination:" />
  <mx:HSlider id="slider2" snapInterval="1" maximum="100" />
  
</mx:Application>
<!-- Ticore's Blog - http://ticore.blogspot.com/ -->

相關連結:
Flex 技巧 - 將資料綁定封裝起來
Flex 技巧 - BindingManager 使用方式
Flex 技巧 - 觀察 Data Binding 資料變化
Flex Tip - 在 Data Binding 內使用 [...] 運算子
Flex 2 Bindable Metadata Tag 背後實際作用
Flex 2.0 - 以 ActionScript 3.0 動態設置 Data Binding
Soph-Ware Associates Blog - Data Binding in Flex, Part I
Soph-Ware Associates Blog - Data Binding in Flex, Part II

轉載請註明出處 http://ticore.blogspot.com/2008/06/flex-databinding-manually.html

0 意見 :