摘要:Silverlight 的 DataBindings
Silverlight的Data Bindings:Silverlight 與 ASP.NET Ajax
 文/黃忠成
  不可否認,對於網頁美工人員或是動畫設計師而言,Silverlight提供了Flash以外的一個畫布,令她們可盡情揮灑創意!但對於設計 師而言,Silverlight如何結合資料庫來呈現資料則是更具吸引力的課題,很明顯的!Silverlight 1.0 RC與Silverlight 1.1並不支援Data Binding機制,而且目前對於中文顯示仍存在著問題(下載字型不算是個解法,因為有法律問題),不過如果這個問題獲得解決,Silverlight結 合資料庫後所呈現的效果,相信會給客戶完全不同的網頁操作感受,當然!前提得要Silverlight支援Data Bindings,否則一切都是紙上談兵罷了。要令Silverlight支援Data Bindings說來也不難,只要處理幾個關鍵問題即可。一、資料提供者為何?ASP.NET Ajax的PageMethods/Web Service可以勝任此工作,如下列程式片段所示。
 private static DataTable BuildDataCache()                  {                      if (HttpRuntime.Cache["DataCache_Employees"] != null)                          return HttpRuntime.Cache["DataCache_Employees"] as DataTable;                      else                      {                          using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[              "NorthwindConnectionString"].ConnectionString))                          {                              SqlDataAdapter adapter = new SqlDataAdapter(              "SELECT * FROM Employees ORDER BY EmployeeID", conn);                              DataTable table = new DataTable("Employees");                              adapter.Fill(table);                              HttpRuntime.Cache["DataCache_Employees"] = table;                              return table;                          }                      }                  }                  [WebMethod]                  public static Employee GetData(int index)                  {                      DataTable table = BuildDataCache();                      return new Employee(table.DefaultView[index]);                  }                  [WebMethod]                  public static int GetCount()                  {                      DataTable table = BuildDataCache();                      return table.DefaultView.Count;                  }                  protected void Button1_Click(object sender, EventArgs e)                  {                  }              }              [Serializable]              public class Employee              {                  [NonSerialized]                  private DataRowView _rv = null;                  public string EmployeeID                  {                      get                      {                          return (_rv.Row.IsNull("EmployeeID") ? string.Empty : _rv["EmployeeID"].ToString());                      }                  }                  public string LastName                  {                      get                      {                          return (_rv.Row.IsNull("LastName") ? string.Empty : (string)_rv["LastName"]);                      }                  }                  public string FirstName                  {                      get                      {                          return (_rv.Row.IsNull("FirstName") ? string.Empty : (string)_rv["FirstName"]);                      }                  }                  public string Title                  {                      get                      {                          return (_rv.Row.IsNull("Title") ? string.Empty : (string)_rv["Title"]);                      }                  }                  public string HireDate                  {                      get                      {                          return (_rv.Row.IsNull("HireDate") ?               string.Empty : ((DateTime)_rv["HireDate"]).ToShortDateString());                      }                  }                  public Employee(DataRowView rv)                  {                      _rv = rv;                  }              }  |          
運用ASP.NET Ajax及JSON,我們可以輕易的用JavaScript來取得資料庫的資料,接著只要將資料設定給指定Silverlight控制項即可,這個工作可 以簡單化也可以複雜化,簡單的作法是直接在JavaScript以findName來取得控制項,然後一一設定其屬性值。
 handleLoad: function(plugIn, userContext, rootElement)               {                         this.plugIn = plugIn;                         this.dataContext = rootElement.children.getItem(0);                         PageMethods.GetCount(this.OnSucceeded,this.OnFailed,this);                         this.receiveData(this.currentDataIndex);              },              OnSucceeded: function(result, userContext, methodName)               {                    if (methodName == "GetData")                    {                        userContext.displayData(result);                    }                    else if(methodName == "GetCount")                        userContext.recordCount = result;              },                        OnFailed:function(error, userContext, methodName)               {                     if(error !== null)                      {                        alert(error.get_message());                     }              },              displayData: function(data)              {                    var idCtrl = this.dataContext.findName('txtEmployeeID');                     var firstNameCtrl = this.dataContext.findName('txtLastName');                     var lastNameCtrl = this.dataContext.findName('txtFirstName');                     var titleCtrl = this.dataContext.findName('txtTitle');                     var hireDateCtrl = this.dataContext.findName('txtHireDate');                     var imgCtrl = this.dataContext.findName("imgPhoto");                     idCtrl.text = data.EmployeeID;                     firstNameCtrl.text = data.FirstName;                     lastNameCtrl.text = data.LastName;                     titleCtrl.text = data.Title;                     hireDateCtrl.text = data.HireDate;                     imgCtrl.source = "Default.aspx?ID="+idCtrl.text;                     this.dataContext.findName("imgAnimation").begin();              }  |          
這種做法的缺點是設計師得用JavaScript寫下許多程式碼。另一種做法是利用Silverlight控制項的Tag屬性,指定Binding Expression,然後於JavaScript中巡覽她們來一一指定值,如下列片段所示。
 <Canvasxmlns="http://schemas.microsoft.com/client/2007"                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">                   <CanvasName="DataDemo"Height="600"Width="800"Tag="BindingContext:GetData,GetCount">                         <Canvas.Background>                              <LinearGradientBrush>                                   <GradientStopColor="Yellow"Offset="0.0" />                                   <GradientStopColor="Orange"Offset="0.5" />                                   <GradientStopColor="Red"Offset="1.0" />                              </LinearGradientBrush>                         </Canvas.Background>                         <TextBlockTag="BindingField:EmployeeID;BindingProperty:Text"Name="txtEmployeeID"Width="144"Height="24"Canvas.Left="166"Canvas.Top="23"Text="A00001"TextWrapping="Wrap"/>                         <TextBlockTag="BindingField:LastName;BindingProperty:Text"Name="txtLastName"Width="320"Height="24"Canvas.Left="500"Canvas.Top="23"Text="Alean Company"TextWrapping="Wrap"/>                         <TextBlockTag="BindingField:FirstName;BindingProperty:Text"Name="txtFirstName"Width="320"Height="24"Canvas.Left="166"Canvas.Top="72"Text="Jeffray"TextWrapping="Wrap"/>                         <TextBlockTag="BindingField:Title;BindingProperty:Text"Name="txtTitle"Width="576"Height="24"Canvas.Left="166"Canvas.Top="122"Text="Taipen 101"TextWrapping="Wrap"/>                         <TextBlockTag="BindingField:HireDate;BindingProperty:Text"Name="txtHireDate"Width="576"Height="24"Canvas.Left="166"Canvas.Top="171"Text="2005/3/4"TextWrapping="Wrap"/>                         <ImageName="imgPhoto" Tag="BindingField:EmployeeID;BindingProperty:Source;Format:Default.aspx?ID={0}"Width="357"Height="206"Canvas.Left="400"Canvas.Top="301">                              <Image.Triggers>                                   <EventTriggerRoutedEvent="Image.Loaded">                                         <BeginStoryboard>                                              <StoryboardName="imgAnimation">                                                   <DoubleAnimation                                                    Storyboard.TargetName="imgPhoto"                                                    Storyboard.TargetProperty="Opacity"                                                    From="0.0"To="1.0"Duration="0:0:6"/>                                              </Storyboard>                                         </BeginStoryboard>                                   </EventTrigger>                              </Image.Triggers>                         </Image>                         <TextBlockName="txtLabel1"Width="114"Height="24"Canvas.Left="18"Canvas.Top="23"Text="Employee ID:"TextWrapping="Wrap"/>                         <TextBlockName="txtLabel1_Copy"Width="120"Height="24"Canvas.Left="349"Canvas.Top="23"Text="Last Name:"TextWrapping="Wrap"/>                         <TextBlockName="txtLabel1_Copy1"Width="130"Height="24"Canvas.Left="18"Canvas.Top="72"Text="First Name:"TextWrapping="Wrap"/>                         <TextBlockName="txtLabel1_Copy2"Width="104"Height="24"Canvas.Left="18"Canvas.Top="122"Text="Title :"TextWrapping="Wrap"/>                         <TextBlockName="txtLabel1_Copy3"Width="93"Height="24"Canvas.Left="18"Canvas.Top="171"Text="Hire Date:"TextWrapping="Wrap"/>                   </Canvas>              </Canvas>  |          
那如何解析Tag的Binding Expression並指定值呢?答案是利用Silverlight的JavaScript支援,一一巡覽所有控制項,一一解析Tag,然後再指定值。
 /////////////////////////////////////////////////////////////////////////              // Silverlight Data Binding Helper 0.1              /////////////////////////////////////////////////////////////////////////              if (!window.SilverlightBinding)                   window.SilverlightBinding = {};              SilverlightBinding.BindingData = function(ctrl,bindingExpression)              {                 var bindings = bindingExpression.split(';');                 this.bindingComplete = false;                 this.ctrl = ctrl;                 for(var i = 0; i < bindings.length; i++)                 {                    var temp = bindings[i].split(':');                    if(temp.length != 2)                     {                       this.bindingComplete = false;                       return;                    }                    if(temp[0] == 'BindingField')                       this.bindingField = temp[1];                    else if(temp[0] == 'BindingProperty')                       this.bindingProperty = temp[1];                    else if(temp[0] == 'Format')                       this.format = temp[1];                    }                 this.bindingComplete = true;              }              SilverlightBinding.BindingData.prototype =               {                 updateValue : function(dataItem)                 {                    if(this.bindingComplete)                    {                      if(this.format)                         eval('this.ctrl.'+this.bindingProperty+              " = this.format.replace('{0}',dataItem."+this.bindingField+');');                      else                         eval('this.ctrl.'+this.bindingProperty+' = dataItem.'+this.bindingField+';');                    }                 }              }              SilverlightBinding.BindingContext = function(bindingContainer)              {                 var parseBinding = bindingContainer.tag.split(':');                 this.bindingComplete = false;                 this.bindingContainer = bindingContainer;                 this.bindingControls = new Array();                 this.currentDataIndex = 0;                 this.recordCount = 0;                 if(parseBinding.length == 2 && parseBinding[0] == "BindingContext")                 {                    var bindingMethods = parseBinding[1].split(',');                    if(bindingMethods.length == 2)                    {                       this.bindingMethod = bindingMethods[0];                       this.bindingCountMethod = bindingMethods[1];                       this.bindingComplete = true;                    }                 }                 if(!this.bindingComplete) alert('ERROR,Binding Failed.');              }              SilverlightBinding.BindingContext.prototype =               {                 _childWorker : function(parent,parseParent)                 {                   if(parent.tag && parent.tag != '')                   {                      if(parseParent)                      {                        var bindingData = new SilverlightBinding.BindingData(parent,parent.tag);                        if(bindingData.bindingComplete)                        {                           this.bindingControls.length++;                           this.bindingControls[this.bindingControls.length-1] = bindingData;                        }                        else                          delete bindingData;                      }                      try                      {                         var temp = parent.children;                       }                      catch(err)                      {                         return;                      }                      for(var i = 0; i < parent.children.count; i++)                          this._childWorker(parent.children.getItem(i),true);                   }                    },                 initialize:function()                 {                   this._childWorker(this.bindingContainer,false);                    this._receiveCount();                      this._receiveData(0);                                },                 OnSucceeded: function(result, userContext, methodName)                   {                     if (methodName == userContext.bindingMethod)                     {                                       for(var i = 0; i < userContext.bindingControls.length; i++)                             userContext.bindingControls[i].updateValue(result);                     }                     else if(methodName == userContext.bindingCountMethod)                         userContext.recordCount = result;                  },                            OnFailed:function(error, userContext, methodName)                   {                     if(error !== null)                      {                        alert(error.get_message());                     }                  },                  _receiveData: function(index)                  {                      eval('PageMethods.'+this.bindingMethod+              '(index,this.OnSucceeded,this.OnFailed,this);');                  },                  _receiveCount: function()                  {                      eval('PageMethods.'+this.bindingCountMethod+              '(this.OnSucceeded,this.OnFailed,this);');                  },                  next:function()                  {                      if(this.currentDataIndex+1 >= this.recordCount)                         return;                      this._receiveData(++this.currentDataIndex);                  },                  prev:function()                  {                      if(this.currentDataIndex -1 < 0)                         return;                      this._receiveData(--this.currentDataIndex);                  }              }  |          
使用此JavaScript物件的方法很簡單,只要於handleLoad中建立此物件,然後將Silverlight的根控制項傳入即可。
 handleLoad: function(plugIn, userContext, rootElement)               {                         this.plugIn = plugIn;                         this.dataContext = rootElement.children.getItem(0);                         this.bindingContext =               new SilverlightBinding.BindingContext(rootElement.children.getItem(0));                         this.bindingContext.initialize();              }  |          
切換上下筆時也很簡單。
 function Next()              {                 window.Silverlight.current_DataContext.bindingContext.next();                 window.Silverlight.current_DataContext.dataContext.findName("imgAnimation").begin();              }              function Prev()              {                 window.Silverlight.current_DataContext.bindingContext.prev();                 window.Silverlight.current_DataContext.dataContext.findName("imgAnimation").begin();              }  |          
下圖為執行畫面。
 
看不到炫麗效果啊?哈~~~我承認,我的美工細胞粉差!
 本例下載位址:
 http://www.dreams.idv.tw/~code6421/files/SLDataDemo1.zip