摘要:Silverlight DataBindings for 1.1 (Managed code)
Silverlight DataBindings for 1.1 (Managed code)
 文/黃忠成
    RC1方興未艾,RC2 已在路上了,看來1.0 Release之日不遠了!前面一篇文章利用了PageMethods與JavaScript為Silverlight 1.0RC加上DataBindings的功能,此次舞台換到了Silverlight 1.1 Alpha Refresh及Visual Studio 2008 Beta 2上,與1.1時不同,這次已無法用單一的Web Site模式實作,基於ASP.NET Ajax與Silverlight所使用的CLR Runtime不同,我們必須將Silverlight與ASP.NET Ajax拆開,別誤會!這並非意味你無法將Silverlight與ASP.NET Ajax放在同一個虛擬目錄下,Silverlight 1.1使用的Binary目錄是ClientBin,ASP.NET是Bin,兩者並無衝突,限制只在於你必須將Silverlight與ASP.NET Ajax分成兩個Project來編譯,在Silverlight編譯完成後將.xaml、.js複製到ASP.NET Ajax的專案目錄下,再將.dll複製到ASP.NET Ajax的ClientBin目錄下即可。回到主題,在Silverlight 1.1中,實現Data Bindings除了可以用前一篇文章的JavaScript技巧外,還多了一個選擇,那就是使用C#等Managed的語言,SLDH.js的C#版本 如下。
 SLDH.cs
 /////////////////////////////////////////////////////////////////////////              // Silverlight Data Binding Helper 0.1 for Silverlight 1.1 Alpha Refresh              /////////////////////////////////////////////////////////////////////////              using System;              using System.Text;              using System.IO;              using System.Net;              using System.Reflection;              using System.Linq;              using System.Collections.Generic;              using System.Windows;              using System.Windows.Controls;              using System.Windows.Documents;              using System.Windows.Ink;              using System.Windows.Input;              using System.Windows.Media;              using System.Windows.Media.Animation;              using System.Windows.Shapes;              using System.Windows.Browser.Net;              using System.Windows.Browser.Serialization;              namespace SilverlightDataHelper              {                  public class JSONDataRow                  {                      private List<object> _columns;                      private List<object> _values;                      public object this[int index]                      {                          get                          {                              return _values[index];                          }                      }                      public object this[string name]                      {                          get                          {                              for (int i = 0; i < _columns.Count; i++)                              {                                  if (((string)_columns[i]).Equals(name))                                      return _values[i];                              }                              return null;                          }                      }                      internal JSONDataRow(object[] columns, object[] values)                      {                          _columns = new List<object>(columns);                          _values = new List<object>(values);                      }                  }                  public class BindingData                  {                      private bool _bindingComplete = false;                      private string _bindingProperty = string.Empty;                      private string _bindingField = string.Empty;                      private FrameworkElement _control;                      private string _format = string.Empty;                      private PropertyInfo _cachedProp = null;                      public bool BindingComplete                      {                          get                          {                              return this._bindingComplete;                          }                      }                      public string BindingProperty                      {                          get                          {                              return _bindingProperty;                          }                      }                      public string BindingField                      {                          get                          {                              return _bindingField;                          }                      }                      public FrameworkElement Control                      {                          get                          {                              return _control;                          }                      }                      public string Format                      {                          get                          {                              return _format;                          }                          set                          {                              _format = value;                          }                      }                      public void UpdateValue(JSONDataRow dataItem)                      {                          if (_bindingComplete)                          {                              if (_cachedProp == null)                              {                                  _cachedProp = _control.GetType().GetProperty(_bindingProperty);                                  if (_cachedProp == null)                                      _bindingComplete = false;                              }                              if (_cachedProp != null && Format != string.Empty)                              {                                  if (_cachedProp.PropertyType == typeof(Uri))                                  {                                      Uri uri = new Uri(string.Format(Format,                                             dataItem[BindingField]), UriKind.Relative);                                      _cachedProp.SetValue(_control, uri, null);                                  }                                  else                                      _cachedProp.SetValue(_control, string.Format(Format,              dataItem[BindingField]), null);                              }                              else if (_cachedProp != null)                                  _cachedProp.SetValue(_control, dataItem[BindingField], null);                          }                      }                      public BindingData(FrameworkElement ctrl, string bindingExpression)                      {                          string[] bindings = bindingExpression.Split(';');                          _bindingComplete = false;                          _control = ctrl;                          for (int i = 0; i < bindings.Length; i++)                          {                              string[] temp = bindings[i].Split(':');                              if (temp.Length != 2)                              {                                  _bindingComplete = false;                                  return;                              }                              if (temp[0].ToLower() == "bindingfield")                                  _bindingField = temp[1];                              else if (temp[0].ToLower() == "bindingproperty")                                  _bindingProperty = temp[1];                              else if (temp[0].ToLower() == "format")                                  _format = temp[1];                          }                          if (_bindingField != string.Empty &&                             _bindingProperty != string.Empty)                              _bindingComplete = true;                      }                  }                  public class BindingContext                  {                      private Panel _container;                      private List<BindingData> _bindingControls = null;                      private bool _bindingComplete = false;                      private int _position = 0;                      private int _count = -1;                      private string _serviceUrl = string.Empty;                      private string _bindingMethod = string.Empty;                      private string _bindingCountMethod = string.Empty;                      private string _countMethod = string.Empty;                      public int Position                      {                          get                          {                              return _position;                          }                          set                          {                              if (_position != value && value < Count && value >= 0)                              {                                  _position = value;                                  UpdateBinding();                              }                          }                      }                      public int Count                      {                          get                          {                              return _count;                          }                      }                      public bool BindingComplete                      {                          get                          {                              return _bindingComplete;                          }                      }                      public List<BindingData> BindingControls                      {                          get                          {                              if (_bindingControls == null)                                  _bindingControls = new List<BindingData>();                              return _bindingControls;                          }                      }                      private void ChildWorker(FrameworkElement elem)                      {                          string exprsssion = elem.Tag == null ? string.Empty : elem.Tag;                          BindingData data = new BindingData(elem, exprsssion);                          if (data.BindingComplete)                              BindingControls.Add(data);                          if (elem is Panel)                          {                              Panel pnl = (Panel)elem;                              for (int i = 0; i < pnl.Children.Count; i++)                              {                                  if (pnl.Children[i] is FrameworkElement)                                      ChildWorker((FrameworkElement)pnl.Children[i]);                              }                          }                      }                      private void FetchCount()                      {                          BrowserHttpWebRequest request =                                  new BrowserHttpWebRequest(new Uri(_serviceUrl + "/" +                                                            _bindingCountMethod, UriKind.Relative));                          request.ContentType = "application/json; charset=utf-8";                          request.Method = "POST";                          request.ContentLength = 0;                          request.Referer = System.Windows.Browser.HtmlPage.DocumentUri.AbsolutePath;                          request.Accept = "/*/";                          HttpWebResponse response = request.GetResponse();                          StreamReader sr = new StreamReader(response.GetResponseStream());                          JavaScriptSerializer serializer = new JavaScriptSerializer();                          string data = sr.ReadToEnd();                          _count = serializer.Deserialize<int>(data);                          sr.Close();                          response.Close();                          request.Close();                      }                      private JSONDataRow FetchData(int index)                      {                          BrowserHttpWebRequest request =                              new BrowserHttpWebRequest(new Uri(_serviceUrl + "/" +                                                        _bindingMethod, UriKind.Relative));                          JavaScriptSerializer serializer = new JavaScriptSerializer();                          request.ContentType = "application/json; charset=utf-8";                          request.Method = "POST";                          request.Referer = System.Windows.Browser.HtmlPage.DocumentUri.AbsolutePath;                          request.Accept = "/*/";                          Stream reqStream = request.GetRequestStream();                          byte[] buff = Encoding.UTF8.GetBytes("{index:" + index.ToString() + "}");                          reqStream.Write(buff, 0, buff.Length);                          request.ContentLength = buff.Length;                          HttpWebResponse response = request.GetResponse();                          StreamReader sr = new StreamReader(response.GetResponseStream());                          string data = sr.ReadToEnd();                          sr.Close();                          response.Close();                          request.Close();                          object[] parsedData = serializer.Deserialize<object[]>(data);                          return new JSONDataRow((object[])parsedData[0], (object[])parsedData[1]);                      }                      private void UpdateBinding()                      {                          JSONDataRow row = FetchData(Position);                          foreach (BindingData item in BindingControls)                              item.UpdateValue(row);                      }                      public void Initialize()                      {                          ChildWorker(_container);                          FetchCount();                          UpdateBinding();                      }                      public BindingContext(Panel container)                      {                          _container = container;                          if (_container.Tag == null)                          {                              _bindingComplete = false;                              return;                          }                          string[] parseBinding = container.Tag.Split(':');                          _bindingComplete = false;                          if (parseBinding.Length == 2 && parseBinding[0].ToLower() == "bindingcontext")                          {                              string[] bindingMethods = parseBinding[1].Split(',');                              if (bindingMethods.Length == 3)                              {                                  _serviceUrl = bindingMethods[0];                                  _bindingMethod = bindingMethods[1];                                  _bindingCountMethod = bindingMethods[2];                                  _bindingComplete = true;                              }                          }                          if (_bindingComplete)                              ChildWorker(container);                      }                  }              }  |          
ㄟ...程式碼變長了哦~~~ >"<,在某些情況下,Managed Code不見得比JavaScript簡單吧!只是別忘了,這些程式碼是預先編譯後再下載到客戶端,由Silverlight CLR執行的,就理論上來說,執行效率應該比JavaScript好才對。由於Managed SLDH使用了另一種JSON格式來交換資料,所以.aspx.cs也要做一些調整。
 Default.aspx.cs
 using System;              using System.IO;              using System.Data;              using System.Collections.Generic;              using System.Data.SqlClient;              using System.Configuration;              using System.Linq;              using System.Web;              using System.Web.Security;              using System.Web.UI;              using System.Web.UI.WebControls;              using System.Web.UI.WebControls.WebParts;              using System.Web.UI.HtmlControls;              using System.Xml.Linq;              using System.Web.Services;              public partial class _Default : System.Web.UI.Page              {                  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["              ConnectionString"].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;                          }                      }                  }                  private static List<object> BuildJSONRow(DataRow row)                  {                      List<object> result = new List<object>();                      List<string> columns = new List<string>();                      List<object> values = new List<object>();                      foreach (DataColumn col in row.Table.Columns)                      {                          columns.Add(col.ColumnName);                          values.Add(row.IsNull(col) ? string.Empty : row[col].ToString());                      }                      result.Add(columns);                      result.Add(values);                      return result;                  }                  [WebMethod]                  public static List<object> GetData(int index)                  {                      DataTable table = BuildDataCache();                      return BuildJSONRow(table.DefaultView[index].Row);                  }                  [WebMethod]                  public static int GetCount()                  {                      DataTable table = BuildDataCache();                      return table.DefaultView.Count;                  }                  protected void Page_Load(object sender, EventArgs e)                  {                      if (Request.QueryString["ID"] != null &&                         Request.QueryString["ID"].Length > 0)                      {                          using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[              "ConnectionString"].ConnectionString))                          {                              conn.Open();                              SqlCommand cmd = new SqlCommand(              "SELECT Photo FROM Employees WHERE EmployeeID = @ID", conn);                              cmd.Parameters.AddWithValue("@ID", Request.QueryString["ID"]);                              object data = cmd.ExecuteScalar();                              if (data != null && ((byte[])data).Length > 0)                              {                                  Response.Clear();                                  Response.BufferOutput = true;                                  Response.ContentType = "image/jpeg";                                  MemoryStream ms = new MemoryStream();                                  ms.Write(((byte[])data), 78, ((byte[])data).Length - 78);                                  MemoryStream jpegms = new MemoryStream();                                  System.Drawing.Image.FromStream(ms).Save(jpegms,               System.Drawing.Imaging.ImageFormat.Jpeg);                                  jpegms.Position = 0;                                  Response.OutputStream.Write(jpegms.GetBuffer(), 0, (int)jpegms.Length);                                  ms.Dispose();                                  jpegms.Dispose();                                  Response.Flush();                                  Response.End();                              }                          }                      }                  }              }  |          
當需要做DataBindings時,只需要在.xaml.cs的Page_Loaded事件處理函式中建立此物件即可,見下面程式碼。
 Page.xaml
 <Canvasx:Name="parentCanvas"                      xmlns="http://schemas.microsoft.com/client/2007"                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                      Loaded="Page_Loaded"                      x:Class="SilverlightProject1.Page;assembly=ClientBin/SilverlightProject1.dll"                      Width="640"                      Height="480"                      Background="White"                      >                         <CanvasName="DataDemo"Height="600"Width="800"               Tag="BindingContext:Default.aspx,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>                         <TextBlockCanvas.Left="100"Canvas.Top="200"              Text="Prev"MouseLeftButtonDown="OnPrevClick"/>                         <TextBlockCanvas.Left="150"Canvas.Top="200"              Text="Next"MouseLeftButtonDown="OnNextClick"/>              </Canvas>  |          
Page.xaml.cs
 using System;              using System.Linq;              using System.Collections.Generic;              using System.Windows;              using System.Windows.Controls;              using System.Windows.Documents;              using System.Windows.Ink;              using System.Windows.Input;              using System.Windows.Media;              using System.Windows.Media.Animation;              using System.Windows.Shapes;              namespace SilverlightProject1              {                  public partial class Page : Canvas                  {                      private SilverlightDataHelper.BindingContext _context = null;                      public void Page_Loaded(object o, EventArgs e)                      {                          // Required to initialize variables                          InitializeComponent();                          _context = new SilverlightDataHelper.BindingContext(FindName("DataDemo") as Panel);                          _context.Initialize();                      }                      void OnPrevClick(object sender, EventArgs args)                      {                          if (_context.Position > 0)                          {                              _context.Position--;                              ((Storyboard)FindName("imgAnimation")).Begin();                          }                      }                      void OnNextClick(object sender, EventArgs args)                      {                          if (_context.Position < _context.Count)                          {                              _context.Position++;                              ((Storyboard)FindName("imgAnimation")).Begin();                          }                      }                  }              }  |          
下圖是執行畫面。
 
(你需要將Northwind.mdf、Northwind_log.ldf複製到App_Data目錄下,或是修改web.config中的ConnectionString來連結到北風資料庫)