摘要: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